From aef2ab453a8b11984c4fe64ba27612a1308ee490 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Fri, 8 Aug 2008 00:37:18 +0000 Subject: [PATCH] Initial import. Needs to: * rename packages * update license information --- LICENSE.commons-logging.txt | 177 ++++ LICENSE.jboss-logging.txt | 504 +++++++++++ LICENSE.log4j.txt | 177 ++++ LICENSE.slf4j.txt | 23 + LICENSE.txt | 504 +++++++++++ NOTICE.txt | 56 ++ pom.xml | 378 ++++++++ src/assembly/default.xml | 36 + .../gleamynode/netty/bootstrap/Bootstrap.java | 255 ++++++ .../netty/bootstrap/ClientBootstrap.java | 160 ++++ .../netty/bootstrap/ServerBootstrap.java | 168 ++++ .../netty/bootstrap/package-info.java | 25 + .../netty/buffer/AbstractChannelBuffer.java | 418 +++++++++ .../buffer/BigEndianHeapChannelBuffer.java | 118 +++ .../buffer/ByteBufferBackedChannelBuffer.java | 252 ++++++ .../netty/buffer/ChannelBuffer.java | 516 +++++++++++ .../buffer/ChannelBufferIndexFinder.java | 96 +++ .../buffer/ChannelBufferInputStream.java | 210 +++++ .../buffer/ChannelBufferOutputStream.java | 119 +++ .../netty/buffer/ChannelBuffers.java | 574 ++++++++++++ .../netty/buffer/CompositeChannelBuffer.java | 474 ++++++++++ .../netty/buffer/DuplicatedChannelBuffer.java | 164 ++++ .../netty/buffer/DynamicChannelBuffer.java | 249 ++++++ .../netty/buffer/HeapChannelBuffer.java | 149 ++++ .../buffer/LittleEndianHeapChannelBuffer.java | 118 +++ .../netty/buffer/ReadOnlyChannelBuffer.java | 166 ++++ .../netty/buffer/SlicedChannelBuffer.java | 209 +++++ .../netty/buffer/TruncatedChannelBuffer.java | 200 +++++ .../netty/buffer/WrappedChannelBuffer.java | 29 + .../gleamynode/netty/buffer/package-info.java | 26 + .../netty/channel/AbstractChannel.java | 187 ++++ .../netty/channel/AbstractChannelSink.java | 40 + .../netty/channel/AbstractServerChannel.java | 75 ++ .../net/gleamynode/netty/channel/Channel.java | 67 ++ .../netty/channel/ChannelConfig.java | 40 + .../channel/ChannelDownstreamHandler.java | 32 + .../netty/channel/ChannelEvent.java | 33 + .../netty/channel/ChannelException.java | 48 ++ .../netty/channel/ChannelFactory.java | 33 + .../netty/channel/ChannelFuture.java | 54 ++ .../netty/channel/ChannelFutureListener.java | 50 ++ .../netty/channel/ChannelHandler.java | 29 + .../netty/channel/ChannelHandlerContext.java | 40 + .../netty/channel/ChannelPipeline.java | 69 ++ .../channel/ChannelPipelineCoverage.java | 44 + .../channel/ChannelPipelineException.java | 48 ++ .../netty/channel/ChannelPipelineFactory.java | 31 + .../gleamynode/netty/channel/ChannelSink.java | 33 + .../netty/channel/ChannelState.java | 32 + .../netty/channel/ChannelStateEvent.java | 33 + .../netty/channel/ChannelUpstreamHandler.java | 32 + .../gleamynode/netty/channel/Channels.java | 369 ++++++++ .../netty/channel/ChildChannelStateEvent.java | 29 + .../netty/channel/CompleteChannelFuture.java | 99 +++ .../netty/channel/DefaultChannelEvent.java | 48 ++ .../netty/channel/DefaultChannelFuture.java | 326 +++++++ .../netty/channel/DefaultChannelPipeline.java | 548 ++++++++++++ .../channel/DefaultChannelStateEvent.java | 97 +++ .../DefaultChildChannelStateEvent.java | 51 ++ .../netty/channel/DefaultExceptionEvent.java | 42 + .../netty/channel/DefaultMessageEvent.java | 56 ++ .../netty/channel/ExceptionEvent.java | 29 + .../netty/channel/FailedChannelFuture.java | 40 + .../netty/channel/MessageEvent.java | 32 + .../netty/channel/SimpleChannelHandler.java | 141 +++ .../netty/channel/SucceededChannelFuture.java | 34 + .../netty/channel/package-info.java | 32 + .../socket/ClientSocketChannelFactory.java | 33 + .../DefaultServerSocketChannelConfig.java | 134 +++ .../socket/DefaultSocketChannelConfig.java | 221 +++++ .../channel/socket/ServerSocketChannel.java | 36 + .../socket/ServerSocketChannelConfig.java | 30 + .../socket/ServerSocketChannelFactory.java | 33 + .../netty/channel/socket/SocketChannel.java | 36 + .../channel/socket/SocketChannelConfig.java | 39 + .../nio/DefaultNioSocketChannelConfig.java | 85 ++ .../DefaultReceiveBufferSizePredictor.java | 67 ++ .../socket/nio/NioAcceptedSocketChannel.java | 60 ++ .../socket/nio/NioClientSocketChannel.java | 91 ++ .../nio/NioClientSocketChannelFactory.java | 56 ++ .../nio/NioClientSocketPipelineSink.java | 303 +++++++ .../socket/nio/NioServerSocketChannel.java | 106 +++ .../nio/NioServerSocketChannelFactory.java | 59 ++ .../nio/NioServerSocketPipelineSink.java | 228 +++++ .../channel/socket/nio/NioSocketChannel.java | 108 +++ .../socket/nio/NioSocketChannelConfig.java | 51 ++ .../netty/channel/socket/nio/NioWorker.java | 480 +++++++++++ .../nio/ReceiveBufferSizePredictor.java | 23 + .../channel/socket/nio/package-info.java | 23 + .../socket/oio/OioAcceptedSocketChannel.java | 86 ++ .../socket/oio/OioClientSocketChannel.java | 72 ++ .../oio/OioClientSocketChannelFactory.java | 40 + .../oio/OioClientSocketPipelineSink.java | 145 ++++ .../socket/oio/OioServerSocketChannel.java | 107 +++ .../oio/OioServerSocketChannelFactory.java | 47 + .../oio/OioServerSocketPipelineSink.java | 224 +++++ .../channel/socket/oio/OioSocketChannel.java | 104 +++ .../netty/channel/socket/oio/OioWorker.java | 160 ++++ .../channel/socket/oio/package-info.java | 23 + .../netty/channel/socket/package-info.java | 22 + .../netty/example/discard/DiscardClient.java | 64 ++ .../netty/example/discard/DiscardHandler.java | 114 +++ .../netty/example/discard/DiscardServer.java | 48 ++ .../example/discard/ThroughputMonitor.java | 49 ++ .../netty/example/echo/EchoClient.java | 67 ++ .../netty/example/echo/EchoHandler.java | 93 ++ .../netty/example/echo/EchoServer.java | 48 ++ .../netty/example/echo/ThroughputMonitor.java | 49 ++ .../example/factorial/BigIntegerDecoder.java | 55 ++ .../example/factorial/FactorialClient.java | 77 ++ .../factorial/FactorialClientHandler.java | 116 +++ .../FactorialClientPipelineFactory.java | 52 ++ .../factorial/FactorialProtocolException.java | 46 + .../example/factorial/FactorialServer.java | 44 + .../factorial/FactorialServerHandler.java | 83 ++ .../FactorialServerPipelineFactory.java | 46 + .../example/factorial/NumberEncoder.java | 76 ++ .../example/objectecho/ObjectEchoClient.java | 67 ++ .../example/objectecho/ObjectEchoHandler.java | 106 +++ .../example/objectecho/ObjectEchoServer.java | 48 ++ .../example/objectecho/ThroughputMonitor.java | 48 ++ .../example/securechat/SecureChatClient.java | 91 ++ .../securechat/SecureChatClientHandler.java | 71 ++ .../securechat/SecureChatKeyStore.java | 310 +++++++ .../securechat/SecureChatPipelineFactory.java | 77 ++ .../example/securechat/SecureChatServer.java | 45 + .../securechat/SecureChatServerHandler.java | 126 +++ .../SecureChatSslContextFactory.java | 86 ++ .../SecureChatTrustManagerFactory.java | 77 ++ .../netty/example/telnet/TelnetClient.java | 91 ++ .../example/telnet/TelnetClientHandler.java | 61 ++ .../example/telnet/TelnetPipelineFactory.java | 59 ++ .../netty/example/telnet/TelnetServer.java | 45 + .../example/telnet/TelnetServerHandler.java | 95 ++ .../frame/DelimiterBasedFrameDecoder.java | 134 +++ .../netty/handler/codec/frame/Delimiters.java | 47 + .../codec/frame/FixedLengthFrameDecoder.java | 52 ++ .../handler/codec/frame/FrameDecoder.java | 144 ++++ .../codec/frame/TooLongFrameException.java | 47 + .../handler/codec/frame/package-info.java | 24 + .../handler/codec/replay/ReplayError.java | 35 + .../codec/replay/ReplayingDecoder.java | 170 ++++ .../codec/replay/ReplayingDecoderBuffer.java | 489 +++++++++++ .../replay/UnsafeDynamicChannelBuffer.java | 45 + .../netty/handler/codec/replay/VoidEnum.java | 29 + .../handler/codec/replay/package-info.java | 23 + .../CompactObjectInputStream.java | 87 ++ .../CompactObjectOutputStream.java | 56 ++ .../CompatibleObjectDecoder.java | 83 ++ .../CompatibleObjectDecoderState.java | 30 + .../CompatibleObjectEncoder.java | 92 ++ .../codec/serialization/ObjectDecoder.java | 85 ++ .../ObjectDecoderInputStream.java | 189 ++++ .../codec/serialization/ObjectEncoder.java | 79 ++ .../ObjectEncoderOutputStream.java | 144 ++++ .../codec/serialization/package-info.java | 26 + .../handler/codec/string/StringDecoder.java | 76 ++ .../handler/codec/string/StringEncoder.java | 74 ++ .../handler/codec/string/package-info.java | 23 + .../execution/ChannelEventRunnable.java | 43 + .../execution/DefaultObjectSizeEstimator.java | 123 +++ .../handler/execution/ExecutionHandler.java | 57 ++ .../MemoryAwareThreadPoolExecutor.java | 237 +++++ .../execution/ObjectSizeEstimator.java | 22 + .../OrderedMemoryAwareThreadPoolExecutor.java | 131 +++ .../netty/handler/execution/package-info.java | 25 + .../netty/handler/ssl/SslBufferPool.java | 79 ++ .../netty/handler/ssl/SslHandler.java | 540 ++++++++++++ .../netty/handler/ssl/package-info.java | 23 + .../netty/logging/CommonsLoggerFactory.java | 101 +++ .../netty/logging/JBossLoggerFactory.java | 103 +++ .../netty/logging/JdkLoggerFactory.java | 102 +++ .../netty/logging/Log4JLoggerFactory.java | 101 +++ .../net/gleamynode/netty/logging/Logger.java | 49 ++ .../netty/logging/LoggerFactory.java | 42 + .../netty/logging/Slf4JLoggerFactory.java | 101 +++ .../netty/logging/package-info.java | 23 + .../gleamynode/netty/util/ConvertUtil.java | 80 ++ .../netty/util/ImmediateExecutor.java | 36 + .../gleamynode/netty/util/MapBackedSet.java | 79 ++ .../netty/util/NamePreservingRunnable.java | 62 ++ .../netty/util/SwitchableInputStream.java | 39 + .../netty/util/TimeBasedUuidGenerator.java | 139 +++ src/site/template.vm | 13 + .../buffer/AbstractChannelBufferTest.java | 815 ++++++++++++++++++ .../BigEndianHeapChannelBufferTest.java | 40 + .../netty/buffer/ChannelBuffersTest.java | 158 ++++ .../buffer/CompositeChannelBufferTest.java | 62 ++ .../netty/buffer/DirectChannelBufferTest.java | 40 + .../buffer/DuplicateChannelBufferTest.java | 40 + .../LittleEndianHeapChannelBufferTest.java | 39 + .../netty/buffer/SlicedChannelBufferTest.java | 42 + .../buffer/TruncatedChannelBufferTest.java | 41 + 193 files changed, 21445 insertions(+) create mode 100644 LICENSE.commons-logging.txt create mode 100644 LICENSE.jboss-logging.txt create mode 100644 LICENSE.log4j.txt create mode 100644 LICENSE.slf4j.txt create mode 100644 LICENSE.txt create mode 100644 NOTICE.txt create mode 100644 pom.xml create mode 100644 src/assembly/default.xml create mode 100644 src/main/java/net/gleamynode/netty/bootstrap/Bootstrap.java create mode 100644 src/main/java/net/gleamynode/netty/bootstrap/ClientBootstrap.java create mode 100644 src/main/java/net/gleamynode/netty/bootstrap/ServerBootstrap.java create mode 100644 src/main/java/net/gleamynode/netty/bootstrap/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/AbstractChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ByteBufferBackedChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ChannelBufferIndexFinder.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ChannelBufferInputStream.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ChannelBufferOutputStream.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ChannelBuffers.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/CompositeChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/DuplicatedChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/DynamicChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/HeapChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/ReadOnlyChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/SlicedChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/TruncatedChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/WrappedChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/buffer/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/channel/AbstractChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/AbstractChannelSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/AbstractServerChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/Channel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelDownstreamHandler.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelException.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelFuture.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelFutureListener.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelHandler.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelHandlerContext.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelPipeline.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelPipelineCoverage.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelPipelineException.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelPipelineFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelState.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelStateEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChannelUpstreamHandler.java create mode 100644 src/main/java/net/gleamynode/netty/channel/Channels.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ChildChannelStateEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/CompleteChannelFuture.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultChannelEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultChannelFuture.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultChannelPipeline.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultChannelStateEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultChildChannelStateEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultExceptionEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/DefaultMessageEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/ExceptionEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/FailedChannelFuture.java create mode 100644 src/main/java/net/gleamynode/netty/channel/MessageEvent.java create mode 100644 src/main/java/net/gleamynode/netty/channel/SimpleChannelHandler.java create mode 100644 src/main/java/net/gleamynode/netty/channel/SucceededChannelFuture.java create mode 100644 src/main/java/net/gleamynode/netty/channel/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/ClientSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/DefaultServerSocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/DefaultSocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/SocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/SocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultReceiveBufferSizePredictor.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioAcceptedSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketPipelineSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketPipelineSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannelConfig.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/NioWorker.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/ReceiveBufferSizePredictor.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/nio/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioAcceptedSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketPipelineSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannelFactory.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketPipelineSink.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioSocketChannel.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/OioWorker.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/oio/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/channel/socket/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/example/discard/DiscardClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/discard/DiscardHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/discard/DiscardServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/discard/ThroughputMonitor.java create mode 100644 src/main/java/net/gleamynode/netty/example/echo/EchoClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/echo/EchoHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/echo/EchoServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/echo/ThroughputMonitor.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/BigIntegerDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialClientHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialClientPipelineFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialProtocolException.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialServerHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/FactorialServerPipelineFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/factorial/NumberEncoder.java create mode 100644 src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/objectecho/ThroughputMonitor.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatClientHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatKeyStore.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatPipelineFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatServerHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatSslContextFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/securechat/SecureChatTrustManagerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/telnet/TelnetClient.java create mode 100644 src/main/java/net/gleamynode/netty/example/telnet/TelnetClientHandler.java create mode 100644 src/main/java/net/gleamynode/netty/example/telnet/TelnetPipelineFactory.java create mode 100644 src/main/java/net/gleamynode/netty/example/telnet/TelnetServer.java create mode 100644 src/main/java/net/gleamynode/netty/example/telnet/TelnetServerHandler.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/DelimiterBasedFrameDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/Delimiters.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/FixedLengthFrameDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/FrameDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/TooLongFrameException.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/frame/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayError.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoderBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/VoidEnum.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/replay/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectInputStream.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectOutputStream.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoderState.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectEncoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoderInputStream.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoderOutputStream.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/serialization/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/string/StringDecoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/string/StringEncoder.java create mode 100644 src/main/java/net/gleamynode/netty/handler/codec/string/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/ChannelEventRunnable.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/DefaultObjectSizeEstimator.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/ExecutionHandler.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/MemoryAwareThreadPoolExecutor.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/ObjectSizeEstimator.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java create mode 100644 src/main/java/net/gleamynode/netty/handler/execution/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/handler/ssl/SslBufferPool.java create mode 100644 src/main/java/net/gleamynode/netty/handler/ssl/SslHandler.java create mode 100644 src/main/java/net/gleamynode/netty/handler/ssl/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/logging/CommonsLoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/JBossLoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/JdkLoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/Log4JLoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/Logger.java create mode 100644 src/main/java/net/gleamynode/netty/logging/LoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/Slf4JLoggerFactory.java create mode 100644 src/main/java/net/gleamynode/netty/logging/package-info.java create mode 100644 src/main/java/net/gleamynode/netty/util/ConvertUtil.java create mode 100644 src/main/java/net/gleamynode/netty/util/ImmediateExecutor.java create mode 100644 src/main/java/net/gleamynode/netty/util/MapBackedSet.java create mode 100644 src/main/java/net/gleamynode/netty/util/NamePreservingRunnable.java create mode 100644 src/main/java/net/gleamynode/netty/util/SwitchableInputStream.java create mode 100644 src/main/java/net/gleamynode/netty/util/TimeBasedUuidGenerator.java create mode 100644 src/site/template.vm create mode 100644 src/test/java/net/gleamynode/netty/buffer/AbstractChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/ChannelBuffersTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/CompositeChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/DirectChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/DuplicateChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/SlicedChannelBufferTest.java create mode 100644 src/test/java/net/gleamynode/netty/buffer/TruncatedChannelBufferTest.java diff --git a/LICENSE.commons-logging.txt b/LICENSE.commons-logging.txt new file mode 100644 index 000000000000..66a27ec5ff94 --- /dev/null +++ b/LICENSE.commons-logging.txt @@ -0,0 +1,177 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/LICENSE.jboss-logging.txt b/LICENSE.jboss-logging.txt new file mode 100644 index 000000000000..d80fdbe771e5 --- /dev/null +++ b/LICENSE.jboss-logging.txt @@ -0,0 +1,504 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/LICENSE.log4j.txt b/LICENSE.log4j.txt new file mode 100644 index 000000000000..66a27ec5ff94 --- /dev/null +++ b/LICENSE.log4j.txt @@ -0,0 +1,177 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/LICENSE.slf4j.txt b/LICENSE.slf4j.txt new file mode 100644 index 000000000000..777b1de84510 --- /dev/null +++ b/LICENSE.slf4j.txt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2004-2007 QOS.ch + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 000000000000..d80fdbe771e5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,504 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 000000000000..465b91214e9e --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,56 @@ + + Netty + + Copyright (C) 2008 Trustin Heuiseung Lee + +-------------------------------------------------------------------------- + +This product includes software developed by: + + * Trustin Heuiseung Lee (http://gleamynode.net/) + +Please visit Netty web site for more information: + + * http://www.jboss.org/netty/ + +-------------------------------------------------------------------------- + +This product is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This product is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + +-------------------------------------------------------------------------- + +Please refer to each LICENSE..txt file for the +license terms of the components that Netty depends on. + +This product optionally depends on 'SLF4J', a simple logging facade for Java, +which can be obtained at: + + * http://www.slf4j.org/ + +This product optionally depends on 'Apache Commons Logging', a logging +framework, which can be obtained at: + + * http://commons.apache.org/logging/ + +This product optionally depends on 'Apache Log4J', a logging framework, +which can be obtained at: + + * http://logging.apache.org/log4j/ + +This product optionally depends on 'JBoss Logging', a logging framework, +which can be obtained at: + + * http://anonsvn.jboss.org/repos/common/common-logging-spi/ + diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000000..ce06a5e22236 --- /dev/null +++ b/pom.xml @@ -0,0 +1,378 @@ + + + + 4.0.0 + org.jboss.netty + netty + 3.0.0.M8-SNAPSHOT + bundle + + The Netty Project + + The Netty project is an effort to provide an asynchronous / event-driven network application framework for rapid development of high-performance / high-scalability protocol servers and clients, including its related out-of-the-box protocol extensions and tool suite. + + + http://www.jboss.org/netty/ + + JBoss.org + http://www.jboss.org/ + + + 2008 + + + + GNU Lesser General Public License + http://www.gnu.org/licenses/lgpl.html + + + + + scm:svn:http://anonsvn.jboss.org/repos/netty/trunk + scm:svn:https://svn.jboss.org/repos/netty/trunk + + + + + + apiviz.release + APIviz releases + http://apiviz.googlecode.com/svn/site/repo/mvn/release + + true + + + false + + + + + + jboss.release + JBoss releases + http://repository.jboss.org/maven2 + + true + + + false + + + + + + + repository.jboss.org + JBoss.org Release Distribution Repository + http://repository.jboss.org/maven2 + + + snapshots.jboss.org + JBoss.org Development Snapshot Repository + http://snapshots.jboss.org/maven2 + + + + + + + org.slf4j + slf4j-api + 1.5.2 + compile + true + + + commons-logging + commons-logging + 1.1.1 + compile + true + + + org.jboss.logging + jboss-logging-spi + 2.0.5.GA + compile + true + + + log4j + log4j + 1.2.15 + compile + true + + + javax.mail + mail + + + javax.jms + jms + + + com.sun.jdmk + jmxtools + + + com.sun.jmx + jmxri + + + + + + + junit + junit + 4.4 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + UTF-8 + 1.5 + 1.5 + true + true + true + + + + org.apache.maven.plugins + maven-pmd-plugin + + + run-pmd + compile + + check + cpd-check + + + + + UTF-8 + 1.5 + false + false + + + + maven-surefire-plugin + + + **/Abstract* + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + run-coverage-test + test + + cobertura + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.groupId} + ${project.groupId}.buffer.*,${project.groupId}.channel.*,${project.groupId}.bootstrap.*,${project.groupId}.handler.* + ${project.groupId}.util.* + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-source + package + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadoc + package + + jar + + + + + net.gleamynode.apiviz.APIviz + + net.gleamynode.apiviz + apiviz + 1.1.3 + + true + + -charset UTF-8 + -docencoding UTF-8 + -version + -author + -breakiterator + -linksource + -sourcetab 4 + -windowtitle "${project.name} ${project.version} API Reference" + -doctitle "${project.name} ${project.version} API Reference" + -bottom "Copyright © ${project.inceptionYear}-Present ${project.organization.name}. All Rights Reserved." + -link http://java.sun.com/javase/6/docs/api/ + -group "Low-level data representation" ${project.groupId}.buffer* + -group "Central interface for all I/O operations" ${project.groupId}.channel* + -group "Client & Server bootstrapping utilities" ${project.groupId}.bootstrap* + -group "Reusable I/O event interceptors" ${project.groupId}.handler* + -group "Miscellaneous" ${project.groupId}.logging* + + UTF-8 + en_US + ${project.groupId}.example*:${project.groupId}.util* + + + + org.apache.maven.plugins + maven-jxr-plugin + + UTF-8 + UTF-8 + false + + + + maven-antrun-plugin + + + add-license + package + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + run + + + + + + ant-contrib + ant-contrib + 1.0b2 + + + + + maven-assembly-plugin + + + attach-distribution + package + + attached + + + + + + ${basedir}/src/assembly/default.xml + + true + gnu + + + + maven-release-plugin + + + https://svn.jboss.org/repos/netty/tags + + + + + + + + + + diff --git a/src/assembly/default.xml b/src/assembly/default.xml new file mode 100644 index 000000000000..932537524a61 --- /dev/null +++ b/src/assembly/default.xml @@ -0,0 +1,36 @@ + + + dist + + tar.gz + zip + + false + + + + + **/README* + **/LICENSE* + **/NOTICE* + **/*.txt + **/*.xml + **/src/** + + + **/target/** + **/.*/** + + + + + + target + jar + + ${project.build.finalName}*.jar + + + + + diff --git a/src/main/java/net/gleamynode/netty/bootstrap/Bootstrap.java b/src/main/java/net/gleamynode/netty/bootstrap/Bootstrap.java new file mode 100644 index 000000000000..c0e41f28ba62 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/bootstrap/Bootstrap.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.bootstrap; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; + +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelHandler; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineFactory; +import net.gleamynode.netty.channel.SimpleChannelHandler; +import net.gleamynode.netty.logging.Logger; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.uses net.gleamynode.netty.channel.ChannelFactory + */ +public class Bootstrap { + + private static Logger logger = Logger.getLogger(Bootstrap.class); + + private volatile ChannelFactory factory; + private volatile ChannelPipeline pipeline = pipeline(); + private volatile ChannelPipelineFactory pipelineFactory = pipelineFactory(pipeline); + private volatile Map options = new HashMap(); + + public Bootstrap() { + super(); + } + + public Bootstrap(ChannelFactory channelFactory) { + setFactory(channelFactory); + } + + public ChannelFactory getFactory() { + ChannelFactory factory = this.factory; + if (factory == null) { + throw new IllegalStateException( + "factory is not set yet."); + } + return factory; + } + + public void setFactory(ChannelFactory factory) { + if (this.factory != null) { + throw new IllegalStateException( + "factory can't change once set."); + } + if (factory == null) { + throw new NullPointerException("factory"); + } + this.factory = factory; + } + + public ChannelPipeline getPipeline() { + return pipeline; + } + + public void setPipeline(ChannelPipeline pipeline) { + if (pipeline == null) { + throw new NullPointerException("pipeline"); + } + pipeline = this.pipeline; + pipelineFactory = pipelineFactory(pipeline); + } + + public Map getPipelineAsMap() { + ChannelPipeline pipeline = this.pipeline; + if (pipeline == null) { + throw new IllegalStateException("pipelineFactory in use"); + } + return pipeline.toMap(); + } + + public void setPipelineAsMap(Map pipelineMap) { + if (pipelineMap == null) { + throw new NullPointerException("pipelineMap"); + } + + if (!isOrderedMap(pipelineMap)) { + throw new IllegalArgumentException( + "pipelineMap is not an ordered map. " + + "Please use " + + LinkedHashMap.class.getName() + "."); + } + + ChannelPipeline pipeline = pipeline(); + for(Map.Entry e: pipelineMap.entrySet()) { + pipeline.addLast(e.getKey(), e.getValue()); + } + + setPipeline(pipeline); + } + + public ChannelPipelineFactory getPipelineFactory() { + return pipelineFactory; + } + + public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) { + if (pipelineFactory == null) { + throw new NullPointerException("pipelineFactory"); + } + pipeline = null; + this.pipelineFactory = pipelineFactory; + } + + public Map getOptions() { + return new TreeMap(options); + } + + public void setOptions(Map options) { + if (options == null) { + throw new NullPointerException("options"); + } + this.options = new HashMap(options); + } + + public Object getOption(String key) { + if (key == null) { + throw new NullPointerException("key"); + } + return options.get(key); + } + + public void setOption(String key, Object value) { + if (key == null) { + throw new NullPointerException("key"); + } + if (value == null) { + options.remove(key); + } else { + options.put(key, value); + } + } + + private static boolean isOrderedMap(Map map) { + Class> mapType = getMapClass(map); + if (LinkedHashMap.class.isAssignableFrom(mapType)) { + if (logger.isDebugEnabled()) { + logger.debug(mapType.getSimpleName() + " is an ordered map."); + } + return true; + } + + if (logger.isDebugEnabled()) { + logger.debug( + mapType.getName() + " is not a " + + LinkedHashMap.class.getSimpleName()); + } + + // Detect Apache Commons Collections OrderedMap implementations. + Class type = mapType; + while (type != null) { + for (Class i: type.getInterfaces()) { + if (i.getName().endsWith("OrderedMap")) { + if (logger.isDebugEnabled()) { + logger.debug( + mapType.getSimpleName() + + " is an ordered map (guessed from that it " + + " implements OrderedMap interface.)"); + } + return true; + } + } + type = type.getSuperclass(); + } + + if (logger.isDebugEnabled()) { + logger.debug( + mapType.getName() + + " doesn't implement OrderedMap interface."); + } + + // Last resort: try to create a new instance and test if it maintains + // the insertion order. + logger.debug( + "Last resort; trying to create a new map instance with a " + + "default constructor and test if insertion order is " + + "maintained."); + + Map newMap; + try { + newMap = mapType.newInstance(); + } catch (Exception e) { + if (logger.isDebugEnabled()) { + logger.debug( + "Failed to create a new map instance of '" + + mapType.getName() +"'.", e); + } + return false; + } + + Random rand = new Random(); + List expectedNames = new ArrayList(); + ChannelHandler dummyHandler = new SimpleChannelHandler(); + for (int i = 0; i < 65536; i ++) { + String filterName; + do { + filterName = String.valueOf(rand.nextInt()); + } while (newMap.containsKey(filterName)); + + newMap.put(filterName, dummyHandler); + expectedNames.add(filterName); + + Iterator it = expectedNames.iterator(); + for (Object key: newMap.keySet()) { + if (!it.next().equals(key)) { + if (logger.isDebugEnabled()) { + logger.debug( + "The specified map didn't pass the insertion " + + "order test after " + (i + 1) + " tries."); + } + return false; + } + } + } + + logger.debug("The specified map passed the insertion order test."); + return true; + } + + @SuppressWarnings("unchecked") + private static Class> getMapClass( + Map map) { + return (Class>) map.getClass(); + } +} diff --git a/src/main/java/net/gleamynode/netty/bootstrap/ClientBootstrap.java b/src/main/java/net/gleamynode/netty/bootstrap/ClientBootstrap.java new file mode 100644 index 000000000000..2f689407964c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/bootstrap/ClientBootstrap.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.bootstrap; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.net.SocketAddress; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelPipelineException; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public class ClientBootstrap extends Bootstrap { + + public ClientBootstrap() { + super(); + } + + public ClientBootstrap(ChannelFactory channelFactory) { + super(channelFactory); + } + + public ChannelFuture connect() { + SocketAddress remoteAddress = (SocketAddress) getOption("remoteAddress"); + if (remoteAddress == null) { + throw new IllegalStateException("remoteAddress option is not set."); + } + SocketAddress localAddress = (SocketAddress) getOption("localAddress"); + return connect(remoteAddress, localAddress); + } + + public ChannelFuture connect(SocketAddress remoteAddress) { + if (remoteAddress == null) { + throw new NullPointerException("remotedAddress"); + } + return connect(remoteAddress, null); + } + + public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress) { + + final BlockingQueue futureQueue = + new LinkedBlockingQueue(); + + ChannelPipeline pipeline; + try { + pipeline = getPipelineFactory().getPipeline(); + } catch (Exception e) { + throw new ChannelPipelineException("Failed to initialize a pipeline.", e); + } + + pipeline.addFirst("connector", new Connector(remoteAddress, localAddress, futureQueue)); + + getFactory().newChannel(pipeline); + + // Wait until the future is available. + ChannelFuture future = null; + do { + try { + future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // Ignore + } + } while (future == null); + + pipeline.remove(pipeline.get("connector")); + + return future; + } + + @ChannelPipelineCoverage("one") + private final class Connector extends SimpleChannelHandler { + private final SocketAddress localAddress; + private final BlockingQueue futureQueue; + private final SocketAddress remoteAddress; + private volatile boolean finished = false; + + Connector(SocketAddress remoteAddress, + SocketAddress localAddress, + BlockingQueue futureQueue) { + this.localAddress = localAddress; + this.futureQueue = futureQueue; + this.remoteAddress = remoteAddress; + } + + @Override + public void channelOpen( + ChannelHandlerContext context, + ChannelStateEvent event) { + context.sendUpstream(event); + + // Apply options. + event.getChannel().getConfig().setOptions(getOptions()); + + // Bind or connect. + if (localAddress != null) { + event.getChannel().bind(localAddress); + } else { + futureQueue.offer(event.getChannel().connect(remoteAddress)); + finished = true; + } + } + + @Override + public void channelBound( + ChannelHandlerContext context, + ChannelStateEvent event) { + context.sendUpstream(event); + + // Connect if not connected yet. + if (localAddress != null) { + futureQueue.offer(event.getChannel().connect(remoteAddress)); + finished = true; + } + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + ctx.sendUpstream(e); + if (!finished) { + e.getChannel().close(); + futureQueue.offer(failedFuture(e.getChannel(), e.getCause())); + finished = true; + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/bootstrap/ServerBootstrap.java b/src/main/java/net/gleamynode/netty/bootstrap/ServerBootstrap.java new file mode 100644 index 000000000000..d859802515a1 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/bootstrap/ServerBootstrap.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.bootstrap; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.net.SocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelHandler; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ChildChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public class ServerBootstrap extends Bootstrap { + + private volatile ChannelHandler parentHandler; + + public ServerBootstrap() { + super(); + } + + public ServerBootstrap(ChannelFactory channelFactory) { + super(channelFactory); + } + + public ChannelHandler getParentHandler() { + return parentHandler; + } + + public void setParentHandler(ChannelHandler parentHandler) { + this.parentHandler = parentHandler; + } + + public Channel bind() { + SocketAddress localAddress = (SocketAddress) getOption("localAddress"); + if (localAddress == null) { + throw new IllegalStateException("localAddress option is not set."); + } + return bind(localAddress); + } + + public Channel bind(final SocketAddress localAddress) { + final BlockingQueue futureQueue = + new LinkedBlockingQueue(); + + ChannelPipeline bossPipeline = pipeline(); + bossPipeline.addLast("binder", new Binder(localAddress, futureQueue)); + + ChannelHandler parentHandler = getParentHandler(); + if (parentHandler != null) { + bossPipeline.addLast("userHandler", parentHandler); + } + + Channel channel = getFactory().newChannel(bossPipeline); + + // Wait until the future is available. + ChannelFuture future = null; + do { + try { + future = futureQueue.poll(Integer.MAX_VALUE, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // Ignore + } + } while (future == null); + + // Wait for the future. + future.awaitUninterruptibly(); + if (!future.isSuccess()) { + future.getChannel().close().awaitUninterruptibly(); + throw new ChannelException("Failed to bind to: " + localAddress, future.getCause()); + } + + return channel; + } + + @ChannelPipelineCoverage("one") + private final class Binder extends SimpleChannelHandler { + + private final SocketAddress localAddress; + private final BlockingQueue futureQueue; + private final Map childOptions = + new HashMap(); + + Binder(SocketAddress localAddress, BlockingQueue futureQueue) { + this.localAddress = localAddress; + this.futureQueue = futureQueue; + } + + @Override + public void channelOpen( + ChannelHandlerContext ctx, + ChannelStateEvent evt) { + evt.getChannel().getConfig().setPipelineFactory(getPipelineFactory()); + + // Split options into two categories: parent and child. + Map allOptions = getOptions(); + Map parentOptions = new HashMap(); + for (Entry e: allOptions.entrySet()) { + if (e.getKey().startsWith("child.")) { + childOptions.put( + e.getKey().substring(6), + e.getValue()); + } else if (!e.getKey().equals("pipelineFactory")) { + parentOptions.put(e.getKey(), e.getValue()); + } + } + + // Apply parent options. + evt.getChannel().getConfig().setOptions(parentOptions); + + futureQueue.offer(evt.getChannel().bind(localAddress)); + ctx.sendUpstream(evt); + } + + @Override + public void childChannelOpen( + ChannelHandlerContext ctx, + ChildChannelStateEvent e) throws Exception { + // Apply child options. + e.getChildChannel().getConfig().setOptions(childOptions); + ctx.sendUpstream(e); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + ctx.sendUpstream(e); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/bootstrap/package-info.java b/src/main/java/net/gleamynode/netty/bootstrap/package-info.java new file mode 100644 index 000000000000..ba806f6e4a3c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/bootstrap/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Helper classes which enable an easy implementation of typical client and + * server socket initialization. + * + * @apiviz.landmark + */ +package net.gleamynode.netty.bootstrap; diff --git a/src/main/java/net/gleamynode/netty/buffer/AbstractChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/AbstractChannelBuffer.java new file mode 100644 index 000000000000..b5179f6662fd --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/AbstractChannelBuffer.java @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.util.NoSuchElementException; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public abstract class AbstractChannelBuffer implements ChannelBuffer { + + private int hashCode; + private int readerIndex; + private int writerIndex; + private int markedReaderIndex; + private int markedWriterIndex; + + public int readerIndex() { + return readerIndex; + } + + public void readerIndex(int readerIndex) { + if (readerIndex < 0 || readerIndex > writerIndex) { + throw new IndexOutOfBoundsException(); + } + this.readerIndex = readerIndex; + } + + public int writerIndex() { + return writerIndex; + } + + public void writerIndex(int writerIndex) { + if (writerIndex < readerIndex || writerIndex > capacity()) { + throw new IndexOutOfBoundsException(); + } + this.writerIndex = writerIndex; + } + + public void setIndex(int readerIndex, int writerIndex) { + if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) { + throw new IndexOutOfBoundsException(); + } + this.readerIndex = readerIndex; + this.writerIndex = writerIndex; + } + + public void clear() { + readerIndex = writerIndex = 0; + } + + public boolean readable() { + return readableBytes() > 0; + } + + public boolean writable() { + return writableBytes() > 0; + } + + public int readableBytes() { + return writerIndex - readerIndex; + } + + public int writableBytes() { + return capacity() - writerIndex; + } + + public void markReaderIndex() { + markedReaderIndex = readerIndex; + } + + public void resetReaderIndex() { + readerIndex(markedReaderIndex); + } + + public void markWriterIndex() { + markedWriterIndex = writerIndex; + } + + public void resetWriterIndex() { + writerIndex = markedWriterIndex; + } + + public void discardReadBytes() { + if (readerIndex == 0) { + return; + } + setBytes(0, this, readerIndex, writerIndex - readerIndex); + writerIndex -= readerIndex; + markedReaderIndex = Math.max(markedReaderIndex - readerIndex, 0); + markedWriterIndex = Math.max(markedWriterIndex - readerIndex, 0); + readerIndex = 0; + } + + public void getBytes(int index, byte[] dst) { + getBytes(index, dst, 0, dst.length); + } + + public void getBytes(int index, ChannelBuffer dst) { + getBytes(index, dst, dst.readerIndex(), dst.readableBytes()); + } + + public void setBytes(int index, byte[] src) { + setBytes(index, src, 0, src.length); + } + + public void setBytes(int index, ChannelBuffer src) { + setBytes(index, src, src.readerIndex(), src.readableBytes()); + } + + public byte readByte() { + if (readerIndex == writerIndex) { + throw new IndexOutOfBoundsException(); + } + return getByte(readerIndex ++); + } + + public short readShort() { + checkReadableBytes(2); + short v = getShort(readerIndex); + readerIndex += 2; + return v; + } + + public int readMedium() { + checkReadableBytes(3); + int v = getMedium(readerIndex); + readerIndex += 3; + return v; + } + + public int readInt() { + checkReadableBytes(4); + int v = getInt(readerIndex); + readerIndex += 4; + return v; + } + + public long readLong() { + checkReadableBytes(8); + long v = getLong(readerIndex); + readerIndex += 8; + return v; + } + + public ChannelBuffer readBytes() { + return readBytes(readableBytes()); + } + + public ChannelBuffer readBytes(int length) { + checkReadableBytes(length); + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + ChannelBuffer buf = ChannelBuffers.buffer(length); + buf.writeBytes(this, readerIndex, length); + readerIndex += length; + return buf; + } + + public ChannelBuffer readBytes(ChannelBufferIndexFinder endIndexFinder) { + int endIndex = indexOf(readerIndex, writerIndex, endIndexFinder); + if (endIndex < 0) { + throw new NoSuchElementException(); + } + return readBytes(endIndex); + } + + public void readBytes(byte[] dst, int dstIndex, int length) { + checkReadableBytes(length); + getBytes(readerIndex, dst, dstIndex, length); + readerIndex += length; + } + + public void readBytes(byte[] dst) { + readBytes(dst, 0, dst.length); + } + + public void readBytes(ChannelBuffer dst) { + readBytes(dst, dst.writableBytes()); + } + + public void readBytes(ChannelBuffer dst, int length) { + readBytes(dst, dst.writerIndex(), length); + dst.writerIndex(dst.writerIndex() + length); + } + + public void readBytes(ChannelBuffer dst, int dstIndex, int length) { + checkReadableBytes(length); + getBytes(readerIndex, dst, dstIndex, length); + readerIndex += length; + } + + public void readBytes(ByteBuffer dst) { + int length = dst.remaining(); + checkReadableBytes(length); + getBytes(readerIndex, dst); + readerIndex += length; + } + + public int readBytes(GatheringByteChannel out, int length) + throws IOException { + checkReadableBytes(length); + int readBytes = getBytes(readerIndex, out, length); + readerIndex += readBytes; + return readBytes; + } + + public void readBytes(OutputStream out, int length) throws IOException { + checkReadableBytes(length); + getBytes(readerIndex, out, length); + readerIndex += length; + } + + public void skipBytes(int length) { + int newReaderIndex = readerIndex + length; + if (newReaderIndex > writerIndex) { + throw new IndexOutOfBoundsException(); + } + readerIndex = newReaderIndex; + } + + public int skipBytes(ChannelBufferIndexFinder firstIndexFinder) { + int oldReaderIndex = readerIndex; + int newReaderIndex = indexOf(oldReaderIndex, writerIndex, firstIndexFinder); + if (newReaderIndex < 0) { + throw new NoSuchElementException(); + } + readerIndex(newReaderIndex); + return newReaderIndex - oldReaderIndex; + } + + public void writeByte(byte value) { + setByte(writerIndex ++, value); + } + + public void writeShort(short value) { + setShort(writerIndex, value); + writerIndex += 2; + } + + public void writeMedium(int value) { + setMedium(writerIndex, value); + writerIndex += 3; + } + + public void writeInt(int value) { + setInt(writerIndex, value); + writerIndex += 4; + } + + public void writeLong(long value) { + setLong(writerIndex, value); + writerIndex += 8; + } + + public void writeBytes(byte[] src, int srcIndex, int length) { + setBytes(writerIndex, src, srcIndex, length); + writerIndex += length; + } + + public void writeBytes(byte[] src) { + writeBytes(src, 0, src.length); + } + + public void writeBytes(ChannelBuffer src) { + writeBytes(src, src.readableBytes()); + } + + public void writeBytes(ChannelBuffer src, int length) { + writeBytes(src, src.readerIndex(), length); + src.readerIndex(src.readerIndex() + length); + } + + public void writeBytes(ChannelBuffer src, int srcIndex, int length) { + setBytes(writerIndex, src, srcIndex, length); + writerIndex += length; + } + + public void writeBytes(ByteBuffer src) { + int length = src.remaining(); + setBytes(writerIndex, src); + writerIndex += length; + } + + public void writeBytes(InputStream in, int length) + throws IOException { + setBytes(writerIndex, in, length); + writerIndex += length; + } + + public int writeBytes(ScatteringByteChannel in, int length) + throws IOException { + int writtenBytes = setBytes(writerIndex, in, length); + writerIndex += writtenBytes; + return writtenBytes; + } + + public void writePlaceholder(int length) { + if (length == 0) { + return; + } + if (length < 0) { + throw new IllegalArgumentException( + "length must be 0 or greater than 0."); + } + int nLong = length >>> 3; + int nBytes = length & 7; + for (int i = nLong; i > 0; i --) { + writeLong(0); + } + if (nBytes == 4) { + writeInt(0); + } else if (nBytes < 4) { + for (int i = nBytes; i > 0; i --) { + writeByte((byte) 0); + } + } else { + writeInt(0); + for (int i = nBytes - 4; i > 0; i --) { + writeByte((byte) 0); + } + } + } + + public ChannelBuffer copy() { + return copy(readerIndex, readableBytes()); + } + + public ChannelBuffer slice() { + return slice(readerIndex, readableBytes()); + } + + public ByteBuffer toByteBuffer() { + return toByteBuffer(readerIndex, readableBytes()); + } + + public ByteBuffer[] toByteBuffers() { + return toByteBuffers(readerIndex, readableBytes()); + } + + public ByteBuffer[] toByteBuffers(int index, int length) { + return new ByteBuffer[] { toByteBuffer(index, length) }; + } + + public int indexOf(int fromIndex, int toIndex, byte value) { + return ChannelBuffers.indexOf(this, fromIndex, toIndex, value); + } + + public int indexOf(int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) { + return ChannelBuffers.indexOf(this, fromIndex, toIndex, indexFinder); + } + + @Override + public int hashCode() { + if (hashCode != 0) { + return hashCode; + } + return hashCode = ChannelBuffers.hashCode(this); + } + + protected void clearHashCode() { + hashCode = 0; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ChannelBuffer)) { + return false; + } + return ChannelBuffers.equals(this, (ChannelBuffer) o); + } + + public int compareTo(ChannelBuffer that) { + return ChannelBuffers.compare(this, that); + } + + @Override + public String toString() { + return getClass().getSimpleName() + '(' + + "ridx=" + readerIndex + ", " + + "widx=" + writerIndex + ", " + + "cap=" + capacity() + + ')'; + } + + protected void checkReadableBytes(int minimumReadableBytes) { + if (readableBytes() < minimumReadableBytes) { + throw new IndexOutOfBoundsException(); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBuffer.java new file mode 100644 index 000000000000..4bea54c0830d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBuffer.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.nio.ByteOrder; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public class BigEndianHeapChannelBuffer extends HeapChannelBuffer { + + public BigEndianHeapChannelBuffer(int length) { + super(length); + } + + public BigEndianHeapChannelBuffer(byte[] array) { + super(array); + } + + private BigEndianHeapChannelBuffer(byte[] array, int readerIndex, int writerIndex) { + super(array, readerIndex, writerIndex); + } + + public ByteOrder order() { + return ByteOrder.BIG_ENDIAN; + } + + public short getShort(int index) { + return (short) (array[index] << 8 | array[index+1] & 0xFF); + } + + public int getMedium(int index) { + return (array[index] & 0xff) << 16 | + (array[index+1] & 0xff) << 8 | + (array[index+2] & 0xff) << 0; + } + + public int getInt(int index) { + return (array[index] & 0xff) << 24 | + (array[index+1] & 0xff) << 16 | + (array[index+2] & 0xff) << 8 | + (array[index+3] & 0xff) << 0; + } + + public long getLong(int index) { + return ((long) array[index] & 0xff) << 56 | + ((long) array[index+1] & 0xff) << 48 | + ((long) array[index+2] & 0xff) << 40 | + ((long) array[index+3] & 0xff) << 32 | + ((long) array[index+4] & 0xff) << 24 | + ((long) array[index+5] & 0xff) << 16 | + ((long) array[index+6] & 0xff) << 8 | + ((long) array[index+7] & 0xff) << 0; + } + + public void setShort(int index, short value) { + array[index ] = (byte) (value >>> 8); + array[index+1] = (byte) (value >>> 0); + } + + public void setMedium(int index, int value) { + array[index ] = (byte) (value >>> 16); + array[index+1] = (byte) (value >>> 8); + array[index+2] = (byte) (value >>> 0); + } + + public void setInt(int index, int value) { + array[index ] = (byte) (value >>> 24); + array[index+1] = (byte) (value >>> 16); + array[index+2] = (byte) (value >>> 8); + array[index+3] = (byte) (value >>> 0); + } + + public void setLong(int index, long value) { + array[index ] = (byte) (value >>> 56); + array[index+1] = (byte) (value >>> 48); + array[index+2] = (byte) (value >>> 40); + array[index+3] = (byte) (value >>> 32); + array[index+4] = (byte) (value >>> 24); + array[index+5] = (byte) (value >>> 16); + array[index+6] = (byte) (value >>> 8); + array[index+7] = (byte) (value >>> 0); + } + + public ChannelBuffer duplicate() { + return new BigEndianHeapChannelBuffer(array, readerIndex(), writerIndex()); + } + + public ChannelBuffer copy(int index, int length) { + if (index < 0 || length < 0 || index + length > array.length) { + throw new IndexOutOfBoundsException(); + } + + byte[] copiedArray = new byte[length]; + System.arraycopy(array, index, copiedArray, 0, length); + return new BigEndianHeapChannelBuffer(copiedArray); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ByteBufferBackedChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/ByteBufferBackedChannelBuffer.java new file mode 100644 index 000000000000..b78b1a8515e9 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ByteBufferBackedChannelBuffer.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + + +public class ByteBufferBackedChannelBuffer extends AbstractChannelBuffer { + + private final ByteBuffer buffer; + private final int capacity; + + public ByteBufferBackedChannelBuffer(ByteBuffer buffer) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + + this.buffer = buffer.slice(); + capacity = buffer.remaining(); + writerIndex(capacity); + } + + private ByteBufferBackedChannelBuffer(ByteBufferBackedChannelBuffer buffer) { + this.buffer = buffer.buffer; + capacity = buffer.capacity; + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + public ByteOrder order() { + return buffer.order(); + } + + public int capacity() { + return capacity; + } + + public byte getByte(int index) { + return buffer.get(index); + } + + public short getShort(int index) { + return buffer.getShort(index); + } + + public int getMedium(int index) { + return (getByte(index) & 0xff) << 16 | + (getByte(index+1) & 0xff) << 8 | + (getByte(index+2) & 0xff) << 0; + } + + public int getInt(int index) { + return buffer.getInt(index); + } + + public long getLong(int index) { + return buffer.getLong(index); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + if (dst instanceof ByteBufferBackedChannelBuffer) { + ByteBufferBackedChannelBuffer bbdst = (ByteBufferBackedChannelBuffer) dst; + ByteBuffer data = bbdst.buffer.duplicate(); + + data.limit(dstIndex + length).position(dstIndex); + getBytes(index, data); + } else if (buffer.hasArray()) { + dst.setBytes(dstIndex, buffer.array(), index + buffer.arrayOffset(), length); + } else { + dst.setBytes(dstIndex, this, index, length); + } + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + ByteBuffer data = buffer.duplicate(); + try { + data.limit(index + length).position(index); + } catch (IllegalArgumentException e) { + throw new IndexOutOfBoundsException(); + } + data.get(dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + ByteBuffer data = buffer.duplicate(); + int bytesToCopy = Math.min(capacity() - index, dst.remaining()); + try { + data.limit(index + bytesToCopy).position(index); + } catch (IllegalArgumentException e) { + throw new IndexOutOfBoundsException(); + } + dst.put(data); + } + + public void setByte(int index, byte value) { + buffer.put(index, value); + } + + public void setShort(int index, short value) { + buffer.putShort(index, value); + } + + public void setMedium(int index, int value) { + setByte(index, (byte) (value >>> 16)); + setByte(index+1, (byte) (value >>> 8)); + setByte(index+2, (byte) (value >>> 0)); + } + + public void setInt(int index, int value) { + buffer.putInt(index, value); + } + + public void setLong(int index, long value) { + buffer.putLong(index, value); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + if (src instanceof ByteBufferBackedChannelBuffer) { + ByteBufferBackedChannelBuffer bbsrc = (ByteBufferBackedChannelBuffer) src; + ByteBuffer data = bbsrc.buffer.duplicate(); + + data.limit(srcIndex + length).position(srcIndex); + setBytes(index, data); + } else if (buffer.hasArray()) { + src.getBytes(srcIndex, buffer.array(), index + buffer.arrayOffset(), length); + } else { + src.getBytes(srcIndex, this, index, length); + } + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + ByteBuffer data = buffer.duplicate(); + data.limit(index + length).position(index); + data.put(src, srcIndex, length); + } + + public void setBytes(int index, ByteBuffer src) { + ByteBuffer data = buffer.duplicate(); + data.limit(index + src.remaining()).position(index); + data.put(src); + } + + public void getBytes(int index, OutputStream out, int length) throws IOException { + if (length == 0) { + return; + } + + if (!buffer.isReadOnly() && buffer.hasArray()) { + out.write( + buffer.array(), + index + buffer.arrayOffset(), + length); + } else { + byte[] tmp = new byte[length]; + ((ByteBuffer) buffer.duplicate().position(index)).get(tmp); + out.write(tmp); + } + } + + public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { + if (length == 0) { + return 0; + } + + return out.write((ByteBuffer) buffer.duplicate().position(index).limit(index + length)); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + if (length == 0) { + return; + } + + if (!buffer.isReadOnly() && buffer.hasArray()) { + index += buffer.arrayOffset(); + do { + int readBytes = in.read( + buffer.array(), index, length); + if (readBytes < 0) { + throw new EOFException(); + } + index += readBytes; + length -= readBytes; + } while (length > 0); + } else { + byte[] tmp = new byte[length]; + for (int i = 0; i < tmp.length;) { + int readBytes = in.read(tmp, i, tmp.length - i); + if (readBytes < 0) { + throw new EOFException(); + } + i += readBytes; + } + ((ByteBuffer) buffer.duplicate().position(index)).get(tmp); + } + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + return in.read((ByteBuffer) buffer.duplicate().limit(index + length).position(index)); + } + + public ByteBuffer toByteBuffer(int index, int length) { + if (index == 0 && length == capacity()) { + return buffer.duplicate(); + } else { + return ((ByteBuffer) buffer.duplicate().position(index).limit(index + length)).slice(); + } + } + + public ChannelBuffer slice(int index, int length) { + if (index == 0 && length == capacity()) { + return duplicate(); + } else { + return new ByteBufferBackedChannelBuffer( + ((ByteBuffer) buffer.duplicate().position(index).limit(index + length))); + } + } + + public ChannelBuffer duplicate() { + return new ByteBufferBackedChannelBuffer(this); + } + + public ChannelBuffer copy(int index, int length) { + ByteBuffer src = (ByteBuffer) buffer.duplicate().position(index).limit(index + length); + ByteBuffer dst = buffer.isDirect() ? ByteBuffer.allocateDirect(length) : ByteBuffer.allocate(length); + dst.put(src); + dst.clear(); + return new ByteBufferBackedChannelBuffer(dst); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/ChannelBuffer.java new file mode 100644 index 000000000000..2091ef55bc4f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ChannelBuffer.java @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + +/** + * A random and sequential accessible sequence of zero or more bytes (octets). + * This interface provides an abstract view for one or more primitive byte + * arrays ({@code byte[]}) and {@linkplain ByteBuffer NIO buffers}. + * + *

Creation of a buffer

+ * + * It is common for a user to create a new buffer using {@link ChannelBuffers} + * utility class rather than calling an individual implementation's constructor. + * + *

Random Access Indexing

+ * + * Just like an ordinary primitive byte array, {@link ChannelBuffer} uses + * zero-based indexing. + * It means the index of the first byte is always {@code 0} and the index of + * the last byte is always {@link #capacity() capacity - 1}. For example, to + * iterate all bytes of a buffer, you can do the following, regardless of + * its internal implementation: + * + *
+ * ChannelBuffer buffer = ...;
+ * for (int i = 0; i < buffer.capacity(); i ++) {
+ *     byte b = array.getByte(i);
+ *     System.out.println((char) b);
+ * }
+ * 
+ * + *

Sequential Access Indexing

+ * + * {@link ChannelBuffer} provides two pointer variables to support sequential + * read and write operations - {@link #readerIndex() readerIndex} for a read + * operation and {@link #writerIndex() writerIndex} for a write operation + * respectively. The following diagram shows how a buffer is segmented into + * three areas by the two pointers: + * + *
+ *      +-------------------+------------------+------------------+
+ *      | discardable bytes |  readable bytes  |  writable space  |
+ *      |                   |     (CONTENT)    |                  |
+ *      +-------------------+------------------+------------------+
+ *      |                   |                  |                  |
+ *      0      <=      readerIndex   <=   writerIndex    <=    capacity
+ * 
+ * + *

Readable bytes (the actual 'content' of the buffer)

+ * + * This segment, so called 'the content of a buffer', is where + * the actual data is stored. Any operation whose name starts with + * {@code read} or {@code skip} will get or skip the data at the current + * {@link #readerIndex() readerIndex} and increase it by the number of read + * bytes. If the argument of the read operation is also a {@link ChannelBuffer} + * and no start index is specified, the specified buffer's + * {@link #readerIndex() readerIndex} is increased together. + *

+ * If there's not enough content left, {@link IndexOutOfBoundsException} is + * raised. The default value of newly allocated, wrapped or copied buffer's + * {@link #readerIndex() readerIndex} is {@code 0}. + * + *

+ * // Iterates the readable bytes of a buffer.
+ * ChannelBuffer buffer = ...;
+ * while (buffer.readable()) {
+ *     System.out.println(buffer.readByte());
+ * }
+ * 
+ * + *

Writable space

+ * + * This segment is a undefined space which needs to be filled. Any operation + * whose name ends with {@code write} will write the data at the current + * {@link #writerIndex() writerIndex} and increase it by the number of written + * bytes. If the argument of the write operation is also a {@link ChannelBuffer}, + * and no start index is specified, the specified buffer's + * {@link #readerIndex() readerIndex} is increased together. + *

+ * If there's not enough writable space left, {@link IndexOutOfBoundsException} + * is raised. The default value of newly allocated buffer's + * {@link #writerIndex() writerIndex} is {@code 0}. The default value of + * wrapped or copied buffer's {@link #writerIndex() writerIndex} is the + * {@link #capacity() capacity} of the buffer. + * + *

+ * // Fills the writable space of a buffer with random integers.
+ * ChannelBuffer buffer = ...;
+ * while (buffer.writableBytes() >= 4) {
+ *     buffer.writeInt(random.nextInt());
+ * }
+ * 
+ * + *

Discardable bytes

+ * + * This segment contains the bytes which were read already by a read operation. + * Initially, the size of this segment is {@code 0}, but its size increases up + * to the {@link #writerIndex() writerIndex} as read operations are executed. + * The read bytes can be discarded by calling {@link #discardReadBytes()} to + * reclaim unused area as depicted by the following diagram: + * + *
+ *  BEFORE discardReadBytes()
+ *
+ *      +-------------------+------------------+------------------+
+ *      | discardable bytes |  readable bytes  |  writable space  |
+ *      |                   |     (CONTENT)    |                  |
+ *      +-------------------+------------------+------------------+
+ *      |                   |                  |                  |
+ *      0      <=      readerIndex   <=   writerIndex    <=    capacity
+ *
+ *
+ *  AFTER discardReadBytes()
+ *
+ *      +------------------+--------------------------------------+
+ *      |  readable bytes  |    writable space (got more space)   |
+ *      |     (CONTENT)    |                                      |
+ *      +------------------+--------------------------------------+
+ *      |                  |                                      |
+ * readerIndex (0) <= writerIndex (decreased)        <=        capacity
+ * 
+ * + *

Clearing the buffer indexes

+ * + * You can set both {@link #readerIndex() readerIndex} and + * {@link #writerIndex() writerIndex} to {@code 0} by calling {@link #clear()}. + * It doesn't clear the buffer content (e.g. filling with {@code 0}) but just + * clears the two pointers. Please also note that the semantic of this + * operation is different from {@link ByteBuffer#clear()}. + * + *
+ *  BEFORE clear()
+ *
+ *      +-------------------+------------------+------------------+
+ *      | discardable bytes |  readable bytes  |  writable space  |
+ *      |                   |     (CONTENT)    |                  |
+ *      +-------------------+------------------+------------------+
+ *      |                   |                  |                  |
+ *      0      <=      readerIndex   <=   writerIndex    <=    capacity
+ *
+ *
+ *  AFTER clear()
+ *
+ *      +---------------------------------------------------------+
+ *      |             writable space (got more space)             |
+ *      +---------------------------------------------------------+
+ *      |                                                         |
+ *      0 = readerIndex = writerIndex            <=            capacity
+ * 
+ * + *

Search operations

+ * + * Various {@code indexOf()} methods help you locate an index of a value which + * meets a certain criteria. Complicated dynamic sequential search can be done + * with {@link ChannelBufferIndexFinder} as well as simple static single byte + * search. + * + *

Mark and reset

+ * + * There are two marker indexes in every buffer. One is for storing + * {@link #readerIndex() readerIndex} and the other is for storing + * {@link #writerIndex() writerIndex}. You can always reposition one of the + * two indexes by calling a reset method. It works in a similar fashion to + * the mark and reset methods in {@link InputStream} except that there's no + * {@code readlimit}. + * + *

Derived buffers

+ * + * You can create a view of an existing buffer by calling either + * {@link #duplicate()}, {@link #slice()} or {@link #slice(int, int)}. + * A derived buffer will have an independent {@link #readerIndex() readerIndex}, + * {@link #writerIndex() writerIndex} and marker indexes, while it shares + * other internal data representation, just like a NIO {@link ByteBuffer} does. + *

+ * In case a completely fresh copy of an existing buffer is required, please + * call {@link #copy()} method instead. + * + *

Conversion to existing JDK types

+ * + * Various {@link #toByteBuffer()} and {@link #toByteBuffers()} methods convert + * a {@link ChannelBuffer} into one or more NIO buffers. These methods avoid + * buffer allocation and memory copy whenever possible, but there's no + * guarantee that memory copy will not be involved or that an explicit memory + * copy will be involved. + *

+ * In case you need to convert a {@link ChannelBuffer} into + * an {@link InputStream} or an {@link OutputStream}, please refer to + * {@link ChannelBufferInputStream} and {@link ChannelBufferOutputStream}. + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public interface ChannelBuffer extends Comparable { + + /** + * A buffer whose capacity is {@code 0}. + */ + static ChannelBuffer EMPTY_BUFFER = new BigEndianHeapChannelBuffer(0); + + /** + * Returns the number of bytes (octets) this buffer can contain. + */ + int capacity(); + + /** + * Returns the endianness + * of this buffer. + */ + ByteOrder order(); + + /** + * Returns the {@code readerIndex} of this buffer. + */ + int readerIndex(); + + /** + * Sets the {@code readerIndex} of this buffer. + * + * @throws IndexOutOfBoundsException + * if the specified {@code readerIndex} is less than 0 or + * greater than {@code writerIndex} + */ + void readerIndex(int readerIndex); + + /** + * Returns the {@code writerIndex} of this buffer. + */ + int writerIndex(); + + /** + * Sets the {@code writerIndex} of this buffer. + * + * @throws IndexOutOfBoundsException + * if the specified {@code writerIndex} is less than + * {@code readerIndex} or greater than {@code capacity} + */ + void writerIndex(int writerIndex); + + /** + * Sets the {@code readerIndex} and {@code writerIndex} of this buffer + * in one shot. This method is useful because you don't need to worry + * about the invocation order of {@link #readerIndex(int)} and {@link #writerIndex(int)} + * methods. For example, the following code will fail: + * + *

+     * // Create a buffer whose readerIndex, writerIndex and capacity are
+     * // 0, 0 and 8 respectively.
+     * ChannelBuffer buf = ChannelBuffers.buffer(8);
+     *
+     * // IndexOutOfBoundsException is thrown because the specified
+     * // readerIndex (2) cannot be greater than the current writerIndex (0).
+     * buf.readerIndex(2);
+     * buf.writerIndex(4);
+     * 
+ * + * The following code will also fail: + * + *
+     * // Create a buffer whose readerIndex, writerIndex and capacity are
+     * // 0, 8 and 8 respectively.
+     * ChannelBuffer buf = ChannelBuffers.wrappedBuffer(new byte[8]);
+     *
+     * // readerIndex becomes 8.
+     * buf.readLong();
+     *
+     * // IndexOutOfBoundsException is thrown because the specified
+     * // writerIndex (4) cannot be less than the current readerIndex (8).
+     * buf.writerIndex(4);
+     * buf.readerIndex(2);
+     * 
+ * + * By contrast, {@link #setIndex(int, int)} guarantees that it never + * throws an {@link IndexOutOfBoundsException} as long as the specified + * indexes meets all constraints, regardless what the current index values + * of the buffer are: + * + *
+     * // No matter what the current state of the buffer is, the following
+     * // call always succeeds as long as the capacity of the buffer is not
+     * // less than 4.
+     * buf.setIndex(2, 4);
+     * 
+ * + * @throws IndexOutOfBoundsException + * if the specified {@code readerIndex} is less than 0, + * if the specified {@code writerIndex} is less than the specified + * {@code readerIndex} or if the specified {@code writerIndex} is + * greater than {@code capacity} + */ + void setIndex(int readerIndex, int writerIndex); + + /** + * Returns the number of readable bytes which equals to + * {@code (writerIndex - readerIndex)}. + */ + int readableBytes(); + + /** + * Returns the number of writable bytes which equals to + * {@code (capacity - writerIndex)}. + */ + int writableBytes(); + + /** + * Returns {@code true} if and only if {@link #readableBytes() readableBytes} + * if greater than {@code 0}. + */ + boolean readable(); + + /** + * Returns {@code true} if and only if {@link #writableBytes() writableBytes} + * if greater than {@code 0}. + */ + boolean writable(); + + /** + * Sets the {@code readerIndex} and {@code writerIndex} of this buffer to + * {@code 0}. + * This method is identical to {@link #setIndex(int, int) setIndex(0, 0)}. + *

+ * Please note that the behavior of this method is different + * from that of NIO {@link ByteBuffer}, which sets the {@code limit} to + * the {@code capacity}. + */ + void clear(); + + /** + * Marks the current {@code readerIndex} in this buffer. You can restore + * the marked {@code readerIndex} by calling {@link #resetReaderIndex()}. + * The initial value of the marked {@code readerIndex} is always {@code 0}. + */ + void markReaderIndex(); + + /** + * Repositions the current {@code readerIndex} to the marked {@code readerIndex} + * in this buffer. + * + * @throws IndexOutOfBoundsException + * if the current {@code writerIndex} is less than the marked + * {@code readerIndex} + */ + void resetReaderIndex(); + + /** + * Marks the current {@code writerIndex} in this buffer. You can restore + * the marked {@code writerIndex} by calling {@link #resetWriterIndex()}. + * The initial value of the marked {@code writerIndex} is always {@code 0}. + */ + void markWriterIndex(); + + /** + * Repositions the current {@code writerIndex} to the marked {@code writerIndex} + * in this buffer. + * + * @throws IndexOutOfBoundsException + * if the current {@code readerIndex} is greater than the marked + * {@code writerIndex} + */ + void resetWriterIndex(); + + /** + * Discards the bytes between the 0th index and {@code readerIndex}. + * It moves the bytes between {@code readerIndex} and {@code writerIndex} + * to the 0th index, and sets {@code readerIndex} and {@code writerIndex} + * to {@code 0} and {@code oldWriterIndex - oldReaderIndex} respectively. + *

+ * Please refer to the class documentation for more detailed explanation + * with a diagram. + */ + void discardReadBytes(); + + byte getByte(int index); + short getShort(int index); + int getMedium(int index); + int getInt(int index); + long getLong(int index); + + void getBytes(int index, ChannelBuffer dst); + void getBytes(int index, ChannelBuffer dst, int dstIndex, int length); + void getBytes(int index, byte[] dst); + void getBytes(int index, byte[] dst, int dstIndex, int length); + void getBytes(int index, ByteBuffer dst); + void getBytes(int index, OutputStream out, int length) throws IOException; + int getBytes(int index, GatheringByteChannel out, int length) throws IOException; + + void setByte(int index, byte value); + void setShort(int index, short value); + void setMedium(int index, int value); + void setInt(int index, int value); + void setLong(int index, long value); + + void setBytes(int index, ChannelBuffer src); + void setBytes(int index, ChannelBuffer src, int srcIndex, int length); + void setBytes(int index, byte[] src); + void setBytes(int index, byte[] src, int srcIndex, int length); + void setBytes(int index, ByteBuffer src); + void setBytes(int index, InputStream in, int length) throws IOException; + int setBytes(int index, ScatteringByteChannel in, int length) throws IOException; + + byte readByte(); + short readShort(); + int readMedium(); + int readInt(); + long readLong(); + + ChannelBuffer readBytes(); + ChannelBuffer readBytes(int length); + ChannelBuffer readBytes(ChannelBufferIndexFinder endIndexFinder); + void readBytes(ChannelBuffer dst); + void readBytes(ChannelBuffer dst, int length); + void readBytes(ChannelBuffer dst, int dstIndex, int length); + void readBytes(byte[] dst); + void readBytes(byte[] dst, int dstIndex, int length); + void readBytes(ByteBuffer dst); + void readBytes(OutputStream out, int length) throws IOException; + int readBytes(GatheringByteChannel out, int length) throws IOException; + + void skipBytes(int length); + int skipBytes(ChannelBufferIndexFinder firstIndexFinder); + + void writeByte(byte value); + void writeShort(short value); + void writeMedium(int value); + void writeInt(int value); + void writeLong(long value); + + void writeBytes(ChannelBuffer src); + void writeBytes(ChannelBuffer src, int length); + void writeBytes(ChannelBuffer src, int srcIndex, int length); + void writeBytes(byte[] src); + void writeBytes(byte[] src, int srcIndex, int length); + void writeBytes(ByteBuffer src); + void writeBytes(InputStream in, int length) throws IOException; + int writeBytes(ScatteringByteChannel in, int length) throws IOException; + + void writePlaceholder(int length); + + int indexOf(int fromIndex, int toIndex, byte value); + int indexOf(int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder); + + ChannelBuffer copy(); + ChannelBuffer copy(int index, int length); + ChannelBuffer slice(); + ChannelBuffer slice(int index, int length); + ChannelBuffer duplicate(); + + ByteBuffer toByteBuffer(); + ByteBuffer toByteBuffer(int index, int length); + ByteBuffer[] toByteBuffers(); + ByteBuffer[] toByteBuffers(int index, int length); + + /** + * Returns a hash code which was calculated from the content of this + * buffer. If there's a byte array which is + * {@linkplain #equals(Object) equal to} this array, both arrays should + * return the same value. + */ + int hashCode(); + + /** + * Determines if the content of the specified buffer is identical to the + * content of this array. 'Identical' here means: + *

    + *
  • the size of the contents of the two buffers are same and
  • + *
  • every single byte of the content of the two buffers are same.
  • + *
+ * Please note that it doesn't compare {@link #readerIndex()} nor + * {@link #writerIndex()}. This method also returns {@code false} for + * {@code null} and an object which is not an instance of + * {@link ChannelBuffer} type. + */ + boolean equals(Object obj); + + /** + * Compares the content of the specified buffer to the content of this + * buffer. Comparison is performed in the same manner with the string + * comparison functions of various languages such as {@code strcmp}, + * {@code memcmp} and {@link String#compareTo(String)}. + */ + int compareTo(ChannelBuffer buffer); + + /** + * Returns the string representation of this buffer. This method doesn't + * necessarily return the whole content of the buffer but returns + * the values of the key properties such as {@link #readerIndex()}, + * {@link #writerIndex()} and {@link #capacity()}.. + */ + String toString(); +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ChannelBufferIndexFinder.java b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferIndexFinder.java new file mode 100644 index 000000000000..e64b6f16b3db --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferIndexFinder.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.uses net.gleamynode.netty.buffer.ChannelBuffer + */ +public interface ChannelBufferIndexFinder { + + boolean find(ChannelBuffer buffer, int guessedIndex); + + static ChannelBufferIndexFinder NUL = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) == 0; + } + }; + + static ChannelBufferIndexFinder NOT_NUL = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) != 0; + } + }; + + static ChannelBufferIndexFinder CR = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) == '\r'; + } + }; + + static ChannelBufferIndexFinder NOT_CR = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) != '\r'; + } + }; + + static ChannelBufferIndexFinder LF = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) == '\n'; + } + }; + + static ChannelBufferIndexFinder NOT_LF = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + return buffer.getByte(guessedIndex) != '\n'; + } + }; + + static ChannelBufferIndexFinder CRLF = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + byte b = buffer.getByte(guessedIndex); + return b == '\r' || b == '\n'; + } + }; + + static ChannelBufferIndexFinder NOT_CRLF = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + byte b = buffer.getByte(guessedIndex); + return b != '\r' && b != '\n'; + } + }; + + static ChannelBufferIndexFinder LINEAR_WHITESPACE = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + byte b = buffer.getByte(guessedIndex); + return b == ' ' || b == '\t'; + } + }; + + static ChannelBufferIndexFinder NOT_LINEAR_WHITESPACE = new ChannelBufferIndexFinder() { + public boolean find(ChannelBuffer buffer, int guessedIndex) { + byte b = buffer.getByte(guessedIndex); + return b != ' ' && b != '\t'; + } + }; +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ChannelBufferInputStream.java b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferInputStream.java new file mode 100644 index 000000000000..2f2819c5438b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferInputStream.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @see ChannelBufferOutputStream + * @apiviz.uses net.gleamynode.netty.buffer.ChannelBuffer + */ +public class ChannelBufferInputStream extends InputStream implements DataInput { + + private final ChannelBuffer buffer; + private final int startIndex; + private final int endIndex; + + public ChannelBufferInputStream(ChannelBuffer buffer) { + this(buffer, buffer.readableBytes()); + } + + public ChannelBufferInputStream(ChannelBuffer buffer, int length) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + if (length > buffer.readableBytes()) { + throw new IndexOutOfBoundsException(); + } + + this.buffer = buffer; + startIndex = buffer.readerIndex(); + endIndex = startIndex + length; + buffer.markReaderIndex(); + } + + public int readBytes() { + return buffer.readerIndex() - startIndex; + } + + @Override + public int available() throws IOException { + return endIndex - buffer.readerIndex(); + } + + @Override + public void mark(int readlimit) { + buffer.markReaderIndex(); + } + + @Override + public boolean markSupported() { + return true; + } + + @Override + public int read() throws IOException { + if (!buffer.readable()) { + return -1; + } + return buffer.readByte() & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int available = available(); + if (available == 0) { + return -1; + } + + len = Math.min(available, len); + buffer.readBytes(b, off, len); + return len; + } + + @Override + public void reset() throws IOException { + buffer.resetReaderIndex(); + } + + @Override + public long skip(long n) throws IOException { + if (n > Integer.MAX_VALUE) { + return skipBytes(Integer.MAX_VALUE); + } else { + return skipBytes((int) n); + } + } + + public boolean readBoolean() throws IOException { + int b = read(); + if (b < 0) { + throw new EOFException(); + } + return b != 0; + } + + public byte readByte() throws IOException { + if (!buffer.readable()) { + throw new EOFException(); + } + return buffer.readByte(); + } + + public char readChar() throws IOException { + return (char) readShort(); + } + + public double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + + public float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + + public void readFully(byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + public void readFully(byte[] b, int off, int len) throws IOException { + checkAvailable(len); + buffer.readBytes(b, off, len); + } + + public int readInt() throws IOException { + checkAvailable(4); + return buffer.readInt(); + } + + private final StringBuilder lineBuf = new StringBuilder(); + + public String readLine() throws IOException { + lineBuf.setLength(0); + for (;;) { + int b = read(); + if (b < 0 || b == '\n') { + break; + } + + lineBuf.append((char) b); + } + + while (lineBuf.charAt(lineBuf.length() - 1) == '\r') { + lineBuf.setLength(lineBuf.length() - 1); + } + + return lineBuf.toString(); + } + + public long readLong() throws IOException { + checkAvailable(8); + return buffer.readLong(); + } + + public short readShort() throws IOException { + checkAvailable(2); + return buffer.readShort(); + } + + public String readUTF() throws IOException { + return DataInputStream.readUTF(this); + } + + public int readUnsignedByte() throws IOException { + return readByte() & 0xff; + } + + public int readUnsignedShort() throws IOException { + return readShort() & 0xffff; + } + + public int skipBytes(int n) throws IOException { + int nBytes = Math.min(available(), n); + buffer.skipBytes(n); + return nBytes; + } + + private void checkAvailable(int fieldSize) throws IOException { + if (fieldSize < 0) { + throw new IllegalArgumentException(); + } + if (fieldSize > available()) { + throw new EOFException(); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/buffer/ChannelBufferOutputStream.java b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferOutputStream.java new file mode 100644 index 000000000000..0ac2c8199576 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ChannelBufferOutputStream.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @see ChannelBufferInputStream + * @apiviz.uses net.gleamynode.netty.buffer.ChannelBuffer + */ +public class ChannelBufferOutputStream extends OutputStream implements DataOutput { + + private final ChannelBuffer buffer; + private final DataOutputStream utf8out = new DataOutputStream(this); + + public ChannelBufferOutputStream(ChannelBuffer buffer) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + this.buffer = buffer; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (len == 0) { + return; + } + + buffer.writeBytes(b, off, len); + } + + @Override + public void write(byte[] b) throws IOException { + buffer.writeBytes(b); + } + + @Override + public void write(int b) throws IOException { + buffer.writeByte((byte) b); + } + + public void writeBoolean(boolean v) throws IOException { + write(v? (byte) 1 : (byte) 0); + } + + public void writeByte(int v) throws IOException { + write(v); + } + + public void writeBytes(String s) throws IOException { + int len = s.length(); + for (int i = 0; i < len; i ++) { + write((byte) s.charAt(i)); + } + } + + public void writeChar(int v) throws IOException { + writeShort((short) v); + } + + public void writeChars(String s) throws IOException { + int len = s.length(); + for (int i = 0 ; i < len ; i ++) { + writeChar(s.charAt(i)); + } + } + + public void writeDouble(double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + + public void writeFloat(float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + public void writeInt(int v) throws IOException { + buffer.writeInt(v); + } + + public void writeLong(long v) throws IOException { + buffer.writeLong(v); + } + + public void writeShort(int v) throws IOException { + buffer.writeShort((short) v); + } + + public void writeUTF(String s) throws IOException { + utf8out.writeUTF(s); + } + + public ChannelBuffer buffer() { + return buffer; + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ChannelBuffers.java b/src/main/java/net/gleamynode/netty/buffer/ChannelBuffers.java new file mode 100644 index 000000000000..ef0bc3611b4d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ChannelBuffers.java @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + + +/** + * Creates a new {@link ChannelBuffer} by allocating new space or by wrapping + * or copying existing byte arrays. + * + *

Use static import

+ * This classes is intended to be used with Java 5 static import statement: + * + *
+ * import static net.gleamynode.netty.buffer.ChannelBuffers.*;
+ *
+ * ChannelBuffer heapBuffer = buffer(128);
+ * ChannelBuffer directBuffer = directBuffer(256);
+ * ChannelBuffer dynamicBuffer = dynamicBuffer(512);
+ * ChannelBuffer wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
+ * ChannelBuffer copiedBuffer = copiedBuffer(ByteBuffer.allocate(128));
+ * 
+ * + *

Allocating a new buffer

+ * + * Three buffer types are provided out of the box. + * + *
    + *
  • {@link #buffer(int)} allocates a new fixed-capacity heap buffer.
  • + *
  • {@link #directBuffer(int)} allocates a new fixed-capacity direct buffer.
  • + *
  • {@link #dynamicBuffer(int)} allocates a new dynamic-capacity heap + * buffer, whose capacity increases automatically as needed by a write + * operation.
  • + *
+ * + *

Creating a wrapped buffer

+ * + * Wrapped buffer is a buffer which is a view of one or more existing + * byte arrays or byte buffer. Any changes in the content of the original + * array or buffer will be reflected in the wrapped buffer. Various wrapper + * methods are provided and their name is all {@code wrappedBuffer()}. + * You might want to take a look at this method closely if you want to create + * a buffer which is composed of more than one array to reduce the number of + * memory copy. + * + *

Creating a copied buffer

+ * + * Copied buffer is a deep copy of one or more existing byte arrays or byte + * buffer. Unlike a wrapped buffer, there's no shared data between the + * original arrays and the copied buffer. Various copy methods are provided + * and their name is all {@code copiedBuffer()}. It's also convenient to + * use this operation to merge multiple buffers into one buffer. + * + *

Miscellaneous utility methods

+ * + * This class also provides various utility methods to help implementation + * of a new buffer type, generation of hex dump and swapping an integer's + * byte order. + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.has net.gleamynode.netty.buffer.ChannelBuffer oneway - - creates + */ +public class ChannelBuffers { + + public static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN; + public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN; + + private static final char[][] HEXDUMP_TABLE = new char[65536][]; + + static { + for (int i = 0; i < 65536; i ++) { + HEXDUMP_TABLE[i] = String.format("%04x", i).toCharArray(); + } + } + + public static ChannelBuffer buffer(int length) { + return buffer(BIG_ENDIAN, length); + } + + public static ChannelBuffer buffer(ByteOrder endianness, int length) { + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + if (endianness == BIG_ENDIAN) { + return new BigEndianHeapChannelBuffer(length); + } else if (endianness == LITTLE_ENDIAN) { + return new LittleEndianHeapChannelBuffer(length); + } else { + throw new NullPointerException("endianness"); + } + } + + public static ChannelBuffer directBuffer(int length) { + return directBuffer(BIG_ENDIAN, length); + } + + public static ChannelBuffer directBuffer(ByteOrder endianness, int length) { + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + ChannelBuffer buffer = new ByteBufferBackedChannelBuffer( + ByteBuffer.allocateDirect(length).order(endianness)); + buffer.clear(); + return buffer; + } + + public static ChannelBuffer dynamicBuffer() { + return dynamicBuffer(BIG_ENDIAN, 256); + } + + public static ChannelBuffer dynamicBuffer(int estimatedLength) { + return dynamicBuffer(BIG_ENDIAN, estimatedLength); + } + + public static ChannelBuffer dynamicBuffer(ByteOrder endianness, int estimatedLength) { + return new DynamicChannelBuffer(endianness, estimatedLength); + } + + public static ChannelBuffer wrappedBuffer(byte[] array) { + return wrappedBuffer(BIG_ENDIAN, array); + } + + public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[] array) { + if (array.length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + if (endianness == BIG_ENDIAN) { + return new BigEndianHeapChannelBuffer(array); + } else if (endianness == LITTLE_ENDIAN) { + return new LittleEndianHeapChannelBuffer(array); + } else { + throw new NullPointerException("endianness"); + } + } + + public static ChannelBuffer wrappedBuffer(byte[] array, int offset, int length) { + return wrappedBuffer(BIG_ENDIAN, array, offset, length); + } + + public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[] array, int offset, int length) { + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + if (offset == 0) { + if (length == array.length) { + return wrappedBuffer(endianness, array); + } else { + return new TruncatedChannelBuffer(wrappedBuffer(endianness, array), length); + } + } else { + return new SlicedChannelBuffer(wrappedBuffer(endianness, array), offset, length); + } + } + + public static ChannelBuffer wrappedBuffer(ByteBuffer buffer) { + if (!buffer.hasRemaining()) { + return ChannelBuffer.EMPTY_BUFFER; + } + if (!buffer.isReadOnly() && buffer.hasArray()) { + return wrappedBuffer(buffer.array(), buffer.arrayOffset(),buffer.remaining()); + } else { + return new ByteBufferBackedChannelBuffer(buffer); + } + } + + public static ChannelBuffer wrappedBuffer(ChannelBuffer buffer) { + if (buffer.readable()) { + return buffer.slice(); + } else { + return ChannelBuffer.EMPTY_BUFFER; + } + } + + public static ChannelBuffer wrappedBuffer(byte[]... arrays) { + return wrappedBuffer(BIG_ENDIAN, arrays); + } + + public static ChannelBuffer wrappedBuffer(ByteOrder endianness, byte[]... arrays) { + switch (arrays.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return wrappedBuffer(endianness, arrays[0]); + } + ChannelBuffer[] wrappedBuffers = new ChannelBuffer[arrays.length]; + for (int i = 0; i < arrays.length; i ++) { + wrappedBuffers[i] = wrappedBuffer(endianness, arrays[i]); + } + return wrappedBuffer(wrappedBuffers); + } + + public static ChannelBuffer wrappedBuffer(ChannelBuffer... buffers) { + switch (buffers.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return wrappedBuffer(buffers[0]); + default: + return new CompositeChannelBuffer(buffers); + } + } + + public static ChannelBuffer wrappedBuffer(ByteBuffer... buffers) { + switch (buffers.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return wrappedBuffer(buffers[0]); + } + ChannelBuffer[] wrappedBuffers = new ChannelBuffer[buffers.length]; + for (int i = 0; i < buffers.length; i ++) { + wrappedBuffers[i] = wrappedBuffer(buffers[i]); + } + return wrappedBuffer(wrappedBuffers); + } + + public static ChannelBuffer copiedBuffer(byte[] array) { + return copiedBuffer(BIG_ENDIAN, array); + } + + public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[] array) { + if (array.length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + if (endianness == BIG_ENDIAN) { + return new BigEndianHeapChannelBuffer(array.clone()); + } else if (endianness == LITTLE_ENDIAN) { + return new LittleEndianHeapChannelBuffer(array.clone()); + } else { + throw new NullPointerException("endianness"); + } + } + + public static ChannelBuffer copiedBuffer(byte[] array, int offset, int length) { + return copiedBuffer(BIG_ENDIAN, array, offset, length); + } + + public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[] array, int offset, int length) { + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + byte[] copy = new byte[length]; + System.arraycopy(array, offset, copy, 0, length); + return wrappedBuffer(endianness, copy); + } + + public static ChannelBuffer copiedBuffer(ByteBuffer buffer) { + int length = buffer.remaining(); + if (length == 0) { + return ChannelBuffer.EMPTY_BUFFER; + } + byte[] copy = new byte[length]; + int position = buffer.position(); + try { + buffer.get(copy); + } finally { + buffer.position(position); + } + return wrappedBuffer(buffer.order(), copy); + } + + public static ChannelBuffer copiedBuffer(ChannelBuffer buffer) { + return buffer.copy(); + } + + public static ChannelBuffer copiedBuffer(byte[]... arrays) { + return copiedBuffer(BIG_ENDIAN, arrays); + } + + public static ChannelBuffer copiedBuffer(ByteOrder endianness, byte[]... arrays) { + switch (arrays.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return copiedBuffer(endianness, arrays[0]); + } + + // Merge the specified arrays into one array. + int length = 0; + for (byte[] a: arrays) { + if (Integer.MAX_VALUE - length < a.length) { + throw new IllegalArgumentException( + "The total length of the specified arrays is too big."); + } + length += a.length; + } + + byte[] mergedArray = new byte[length]; + for (int i = 0, j = 0; i < arrays.length; i ++) { + byte[] a = arrays[i]; + System.arraycopy(a, 0, mergedArray, j, a.length); + j += a.length; + } + + return wrappedBuffer(endianness, mergedArray); + } + + public static ChannelBuffer copiedBuffer(ChannelBuffer... buffers) { + switch (buffers.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return copiedBuffer(buffers[0]); + } + + ChannelBuffer[] copiedBuffers = new ChannelBuffer[buffers.length]; + for (int i = 0; i < buffers.length; i ++) { + copiedBuffers[i] = buffers[i].copy(); + } + return wrappedBuffer(copiedBuffers); + } + + public static ChannelBuffer copiedBuffer(ByteBuffer... buffers) { + switch (buffers.length) { + case 0: + return ChannelBuffer.EMPTY_BUFFER; + case 1: + return copiedBuffer(buffers[0]); + } + + ChannelBuffer[] copiedBuffers = new ChannelBuffer[buffers.length]; + for (int i = 0; i < buffers.length; i ++) { + copiedBuffers[i] = wrappedBuffer(buffers[i]).copy(); + } + return wrappedBuffer(copiedBuffers); + } + + public static ChannelBuffer unmodifiableBuffer(ChannelBuffer buffer) { + if (buffer instanceof ReadOnlyChannelBuffer) { + buffer = ((ReadOnlyChannelBuffer) buffer).unwrap(); + } + return new ReadOnlyChannelBuffer(buffer); + } + + public static String hexDump(ChannelBuffer buffer) { + return hexDump(buffer, buffer.readerIndex(), buffer.readableBytes()); + } + + public static String hexDump(ChannelBuffer buffer, int fromIndex, int length) { + if (length < 0) { + throw new IllegalArgumentException("length: " + length); + } + if (length == 0) { + return ""; + } + + int endIndex = fromIndex + (length >>> 1 << 1); + boolean oddLength = length % 2 != 0; + char[] buf = new char[length << 1]; + + int srcIdx = fromIndex; + int dstIdx = 0; + for (; srcIdx < endIndex; srcIdx += 2, dstIdx += 4) { + System.arraycopy( + HEXDUMP_TABLE[buffer.getShort(srcIdx) & 0xFFFF], + 0, buf, dstIdx, 4); + } + + if (oddLength) { + System.arraycopy( + HEXDUMP_TABLE[buffer.getByte(srcIdx) & 0xFF], + 2, buf, dstIdx, 2); + } + + return new String(buf); + } + + public static int hashCode(ChannelBuffer buffer) { + final int aLen = buffer.readableBytes(); + final int intCount = aLen >>> 2; + final int byteCount = aLen & 3; + + int hashCode = 1; + int arrayIndex = buffer.readerIndex(); + for (int i = intCount; i > 0; i --) { + hashCode = 31 * hashCode + buffer.getInt(arrayIndex); + arrayIndex += 4; + } + for (int i = byteCount; i > 0; i --) { + hashCode = 31 * hashCode + buffer.getByte(arrayIndex ++); + } + + if (hashCode == 0) { + hashCode = 1; + } + return hashCode; + } + + public static boolean equals(ChannelBuffer bufferA, ChannelBuffer bufferB) { + final int aLen = bufferA.readableBytes(); + if (aLen != bufferB.readableBytes()) { + return false; + } + + final int longCount = aLen >>> 3; + final int byteCount = aLen & 7; + + int aIndex = bufferA.readerIndex(); + int bIndex = bufferB.readerIndex(); + for (int i = longCount; i > 0; i --) { + if (bufferA.getLong(aIndex) != bufferB.getLong(bIndex)) { + return false; + } + aIndex += 8; + bIndex += 8; + } + + for (int i = byteCount; i > 0; i --) { + if (bufferA.getByte(aIndex) != bufferB.getByte(bIndex)) { + return false; + } + aIndex ++; + bIndex ++; + } + + return true; + } + + public static int compare(ChannelBuffer bufferA, ChannelBuffer bufferB) { + final int aLen = bufferA.readableBytes(); + final int bLen = bufferB.readableBytes(); + final int minLength = Math.min(aLen, bLen); + final int longCount = minLength >>> 3; + final int byteCount = minLength & 7; + + int aIndex = bufferA.readerIndex(); + int bIndex = bufferB.readerIndex(); + for (int i = longCount; i > 0; i --) { + long va = bufferA.getLong(aIndex); + long vb = bufferB.getLong(bIndex); + if (va > vb) { + return 1; + } else if (va < vb) { + return -1; + } + aIndex += 8; + bIndex += 8; + } + + for (int i = byteCount; i > 0; i --) { + byte va = bufferA.getByte(aIndex); + byte vb = bufferB.getByte(bIndex); + if (va > vb) { + return 1; + } else if (va < vb) { + return -1; + } + aIndex ++; + bIndex ++; + } + + return aLen - bLen; + } + + public static int indexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) { + if (fromIndex <= toIndex) { + return firstIndexOf(buffer, fromIndex, toIndex, value); + } else { + return lastIndexOf(buffer, fromIndex, toIndex, value); + } + } + + public static int indexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) { + if (fromIndex <= toIndex) { + return firstIndexOf(buffer, fromIndex, toIndex, indexFinder); + } else { + return lastIndexOf(buffer, fromIndex, toIndex, indexFinder); + } + } + + public static short swapShort(short value) { + return (short) (value << 8 | value >>> 8 & 0xff); + } + + public static int swapMedium(int value) { + return value << 16 & 0xff0000 | value & 0xff00 | value >>> 16 & 0xff; + } + + public static int swapInt(int value) { + return swapShort((short) value) << 16 | + swapShort((short) (value >>> 16)) & 0xffff; + } + + public static long swapLong(long value) { + return (long) swapInt((int) value) << 32 | + swapInt((int) (value >>> 32)) & 0xffffffffL; + } + + private static int firstIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) { + fromIndex = Math.max(fromIndex, 0); + if (fromIndex >= toIndex || buffer.capacity() == 0) { + return -1; + } + + for (int i = fromIndex; i < toIndex; i ++) { + if (buffer.getByte(i) == value) { + return i; + } + } + + return -1; + } + + private static int lastIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, byte value) { + fromIndex = Math.min(fromIndex, buffer.capacity()); + if (fromIndex < 0 || buffer.capacity() == 0) { + return -1; + } + + for (int i = fromIndex - 1; i >= toIndex; i --) { + if (buffer.getByte(i) == value) { + return i; + } + } + + return -1; + } + + private static int firstIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) { + fromIndex = Math.max(fromIndex, 0); + if (fromIndex >= toIndex || buffer.capacity() == 0) { + return -1; + } + + for (int i = fromIndex; i < toIndex; i ++) { + if (indexFinder.find(buffer, i)) { + return i; + } + } + + return -1; + } + + private static int lastIndexOf(ChannelBuffer buffer, int fromIndex, int toIndex, ChannelBufferIndexFinder indexFinder) { + fromIndex = Math.min(fromIndex, buffer.capacity()); + if (fromIndex < 0 || buffer.capacity() == 0) { + return -1; + } + + for (int i = fromIndex - 1; i >= toIndex; i --) { + if (indexFinder.find(buffer, i)) { + return i; + } + } + + return -1; + } + + private ChannelBuffers() { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/CompositeChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/CompositeChannelBuffer.java new file mode 100644 index 000000000000..43c4b67255c4 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/CompositeChannelBuffer.java @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.util.ArrayList; +import java.util.List; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class CompositeChannelBuffer extends AbstractChannelBuffer { + + private final ChannelBuffer[] slices; + private final int[] indices; + private int lastSliceId; + + public CompositeChannelBuffer(ChannelBuffer... buffers) { + if (buffers.length == 0) { + throw new IllegalArgumentException("buffers should not be empty."); + } + + ByteOrder expectedEndianness = buffers[0].order(); + slices = new ChannelBuffer[buffers.length]; + for (int i = 0; i < buffers.length; i ++) { + if (buffers[i].order() != expectedEndianness) { + throw new IllegalArgumentException( + "All buffers must have the same endianness."); + } + slices[i] = buffers[i].slice(); + } + indices = new int[buffers.length + 1]; + for (int i = 1; i <= buffers.length; i ++) { + indices[i] = indices[i - 1] + slices[i - 1].capacity(); + } + writerIndex(capacity()); + } + + private CompositeChannelBuffer(CompositeChannelBuffer buffer) { + slices = buffer.slices.clone(); + indices = buffer.indices.clone(); + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + public ByteOrder order() { + return slices[0].order(); + } + + public int capacity() { + return indices[slices.length]; + } + + public byte getByte(int index) { + int sliceId = sliceId(index); + return slices[sliceId].getByte(index - indices[sliceId]); + } + + public short getShort(int index) { + int sliceId = sliceId(index); + if (index + 2 <= indices[sliceId + 1]) { + return slices[sliceId].getShort(index - indices[sliceId]); + } else if (order() == ByteOrder.BIG_ENDIAN) { + return (short) ((getByte(index) & 0xff) << 8 | getByte(index + 1) & 0xff); + } else { + return (short) (getByte(index) & 0xff | (getByte(index + 1) & 0xff) << 8); + } + } + + public int getMedium(int index) { + int sliceId = sliceId(index); + if (index + 3 <= indices[sliceId + 1]) { + return slices[sliceId].getMedium(index - indices[sliceId]); + } else if (order() == ByteOrder.BIG_ENDIAN) { + return (getShort(index) & 0xffff) << 8 | getByte(index + 2) & 0xff; + } else { + return getShort(index) & 0xFFFF | (getByte(index + 2) & 0xFF) << 16; + } + } + + public int getInt(int index) { + int sliceId = sliceId(index); + if (index + 4 <= indices[sliceId + 1]) { + return slices[sliceId].getInt(index - indices[sliceId]); + } else if (order() == ByteOrder.BIG_ENDIAN) { + return (getShort(index) & 0xffff) << 16 | getShort(index + 2) & 0xffff; + } else { + return getShort(index) & 0xFFFF | (getShort(index + 2) & 0xFFFF) << 16; + } + } + + public long getLong(int index) { + int sliceId = sliceId(index); + if (index + 8 <= indices[sliceId + 1]) { + return slices[sliceId].getLong(index - indices[sliceId]); + } else if (order() == ByteOrder.BIG_ENDIAN) { + return (getInt(index) & 0xffffffffL) << 32 | getInt(index + 4) & 0xffffffffL; + } else { + return getInt(index) & 0xFFFFFFFFL | (getInt(index + 4) & 0xFFFFFFFFL) << 32; + } + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.getBytes(index - adjustment, dst, dstIndex, localLength); + index += localLength; + dstIndex += localLength; + length -= localLength; + i ++; + } + } + + public void getBytes(int index, ByteBuffer dst) { + int sliceId = sliceId(index); + int limit = dst.limit(); + int length = dst.remaining(); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + try { + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + dst.limit(dst.position() + localLength); + s.getBytes(index - adjustment, dst); + index += localLength; + length -= localLength; + i ++; + } + } finally { + dst.limit(limit); + } + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.getBytes(index - adjustment, dst, dstIndex, localLength); + index += localLength; + dstIndex += localLength; + length -= localLength; + i ++; + } + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + // XXX Gathering write is not supported because of a known issue. + // See http://bugs.sun.com/view_bug.do?bug_id=6210541 + return out.write(toByteBuffer()); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.getBytes(index - adjustment, out, localLength); + index += localLength; + length -= localLength; + i ++; + } + } + + public void setByte(int index, byte value) { + int sliceId = sliceId(index); + slices[sliceId].setByte(index - indices[sliceId], value); + } + + public void setShort(int index, short value) { + int sliceId = sliceId(index); + if (index + 2 <= indices[sliceId + 1]) { + slices[sliceId].setShort(index - indices[sliceId], value); + } else if (order() == ByteOrder.BIG_ENDIAN) { + setByte(index, (byte) (value >>> 8)); + setByte(index + 1, (byte) value); + } else { + setByte(index , (byte) value); + setByte(index + 1, (byte) (value >>> 8)); + } + } + + public void setMedium(int index, int value) { + int sliceId = sliceId(index); + if (index + 3 <= indices[sliceId + 1]) { + slices[sliceId].setMedium(index - indices[sliceId], value); + } else if (order() == ByteOrder.BIG_ENDIAN) { + setShort(index, (short) (value >>> 8)); + setByte(index + 2, (byte) value); + } else { + setShort(index , (short) value); + setByte (index + 2, (byte) (value >>> 16)); + } + } + + public void setInt(int index, int value) { + int sliceId = sliceId(index); + if (index + 4 <= indices[sliceId + 1]) { + slices[sliceId].setInt(index - indices[sliceId], value); + } else if (order() == ByteOrder.BIG_ENDIAN) { + setShort(index, (short) (value >>> 16)); + setShort(index + 2, (short) value); + } else { + setShort(index , (short) value); + setShort(index + 2, (short) (value >>> 16)); + } + } + + public void setLong(int index, long value) { + int sliceId = sliceId(index); + if (index + 8 <= indices[sliceId + 1]) { + slices[sliceId].setLong(index - indices[sliceId], value); + } else if (order() == ByteOrder.BIG_ENDIAN) { + setInt(index, (int) (value >>> 32)); + setInt(index + 4, (int) value); + } else { + setInt(index , (int) value); + setInt(index + 4, (int) (value >>> 32)); + } + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.setBytes(index - adjustment, src, srcIndex, localLength); + index += localLength; + srcIndex += localLength; + length -= localLength; + i ++; + } + } + + public void setBytes(int index, ByteBuffer src) { + int sliceId = sliceId(index); + int limit = src.limit(); + int length = src.remaining(); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + try { + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + src.limit(src.position() + localLength); + s.setBytes(index - adjustment, src); + index += localLength; + length -= localLength; + i ++; + } + } finally { + src.limit(limit); + } + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.setBytes(index - adjustment, src, srcIndex, localLength); + index += localLength; + srcIndex += localLength; + length -= localLength; + i ++; + } + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.setBytes(index - adjustment, in, localLength); + index += localLength; + length -= localLength; + i ++; + } + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + int i = sliceId; + int writtenBytes = 0; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + int localWrittenBytes = s.setBytes(index - adjustment, in, localLength); + writtenBytes += localWrittenBytes; + if (localLength != localWrittenBytes) { + break; + } + index += localLength; + length -= localLength; + i ++; + } + + return writtenBytes; + } + + public ChannelBuffer duplicate() { + return new CompositeChannelBuffer(this); + } + + public ChannelBuffer copy(int index, int length) { + int sliceId = sliceId(index); + if (index + length >= capacity()) { + throw new IndexOutOfBoundsException(); + } + + ChannelBuffer dst = ChannelBuffers.buffer(length); + int dstIndex = 0; + int i = sliceId; + + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + s.getBytes(index - adjustment, dst, dstIndex, localLength); + index += localLength; + dstIndex += localLength; + length -= localLength; + i ++; + } + + dst.writerIndex(dst.capacity()); + return dst; + } + + public ChannelBuffer slice(int index, int length) { + return new SlicedChannelBuffer(this, index, length); + } + + public ByteBuffer toByteBuffer(int index, int length) { + ByteBuffer[] buffers = toByteBuffers(index, length); + ByteBuffer merged = ByteBuffer.allocate(length); + for (ByteBuffer b: buffers) { + merged.put(b); + } + merged.flip(); + return merged; + } + + @Override + public ByteBuffer[] toByteBuffers(int index, int length) { + int sliceId = sliceId(index); + if (index + length > capacity()) { + throw new IndexOutOfBoundsException(); + } + + List buffers = new ArrayList(slices.length); + + int i = sliceId; + while (length > 0) { + ChannelBuffer s = slices[i]; + int adjustment = indices[i]; + int localLength = Math.min(length, s.capacity() - (index - adjustment)); + buffers.add(s.toByteBuffer(index - adjustment, localLength)); + index += localLength; + length -= localLength; + i ++; + } + + return buffers.toArray(new ByteBuffer[buffers.size()]); + } + + private int sliceId(int index) { + int lastSliceId = this.lastSliceId; + if (index >= indices[lastSliceId]) { + if (index < indices[lastSliceId + 1]) { + return lastSliceId; + } + + // Search right + for (int i = lastSliceId + 1; i < slices.length; i ++) { + if (index < indices[i + 1]) { + this.lastSliceId = i; + return i; + } + } + } else { + // Search left + for (int i = lastSliceId - 1; i >= 0; i --) { + if (index >= indices[i]) { + this.lastSliceId = i; + return i; + } + } + } + + throw new IndexOutOfBoundsException(); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/DuplicatedChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/DuplicatedChannelBuffer.java new file mode 100644 index 000000000000..c604a615b570 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/DuplicatedChannelBuffer.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class DuplicatedChannelBuffer extends AbstractChannelBuffer implements WrappedChannelBuffer { + + private final ChannelBuffer buffer; + + public DuplicatedChannelBuffer(ChannelBuffer buffer) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + this.buffer = buffer; + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + private DuplicatedChannelBuffer(DuplicatedChannelBuffer buffer) { + this.buffer = buffer.buffer; + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + public ChannelBuffer unwrap() { + return buffer; + } + + public ByteOrder order() { + return buffer.order(); + } + + public int capacity() { + return buffer.capacity(); + } + + public byte getByte(int index) { + return buffer.getByte(index); + } + + public short getShort(int index) { + return buffer.getShort(index); + } + + public int getMedium(int index) { + return buffer.getMedium(index); + } + + public int getInt(int index) { + return buffer.getInt(index); + } + + public long getLong(int index) { + return buffer.getLong(index); + } + + public ChannelBuffer duplicate() { + return new DuplicatedChannelBuffer(this); + } + + public ChannelBuffer copy(int index, int length) { + return buffer.copy(index, length); + } + + public ChannelBuffer slice(int index, int length) { + return buffer.slice(index, length); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + buffer.getBytes(index, dst); + } + + public void setByte(int index, byte value) { + buffer.setByte(index, value); + } + + public void setShort(int index, short value) { + buffer.setShort(index, value); + } + + public void setMedium(int index, int value) { + buffer.setMedium(index, value); + } + + public void setInt(int index, int value) { + buffer.setInt(index, value); + } + + public void setLong(int index, long value) { + buffer.setLong(index, value); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ByteBuffer src) { + buffer.setBytes(index, src); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + buffer.getBytes(index, out, length); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + return buffer.getBytes(index, out, length); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + buffer.setBytes(index, in, length); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + return buffer.setBytes(index, in, length); + } + + public ByteBuffer toByteBuffer(int index, int length) { + return buffer.toByteBuffer(index, length); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/DynamicChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/DynamicChannelBuffer.java new file mode 100644 index 000000000000..6aebe115ade3 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/DynamicChannelBuffer.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class DynamicChannelBuffer extends AbstractChannelBuffer { + + private final int initialCapacity; + private final ByteOrder endianness; + private ChannelBuffer buffer = ChannelBuffer.EMPTY_BUFFER; + + public DynamicChannelBuffer(int estimatedLength) { + this(ByteOrder.BIG_ENDIAN, estimatedLength); + } + + public DynamicChannelBuffer(ByteOrder endianness, int estimatedLength) { + if (estimatedLength <= 0) { + throw new IllegalArgumentException("estimatedLength"); + } + if (endianness == null) { + throw new NullPointerException("endianness"); + } + + initialCapacity = estimatedLength; + this.endianness = endianness; + } + + public ByteOrder order() { + return endianness; + } + + public int capacity() { + return buffer.capacity(); + } + + public byte getByte(int index) { + return buffer.getByte(index); + } + + public short getShort(int index) { + return buffer.getShort(index); + } + + public int getMedium(int index) { + return buffer.getMedium(index); + } + + public int getInt(int index) { + return buffer.getInt(index); + } + + public long getLong(int index) { + return buffer.getLong(index); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + buffer.getBytes(index, dst); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + return buffer.getBytes(index, out, length); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + buffer.getBytes(index, out, length); + } + + public void setByte(int index, byte value) { + buffer.setByte(index, value); + } + + public void setShort(int index, short value) { + buffer.setShort(index, value); + } + + public void setMedium(int index, int value) { + buffer.setMedium(index, value); + } + + public void setInt(int index, int value) { + buffer.setInt(index, value); + } + + public void setLong(int index, long value) { + buffer.setLong(index, value); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ByteBuffer src) { + buffer.setBytes(index, src); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + buffer.setBytes(index, in, length); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + return buffer.setBytes(index, in, length); + } + + @Override + public void writeByte(byte value) { + ensureWritableBytes(1); + super.writeByte(value); + } + + @Override + public void writeShort(short value) { + ensureWritableBytes(2); + super.writeShort(value); + } + + @Override + public void writeMedium(int value) { + ensureWritableBytes(3); + super.writeMedium(value); + } + + @Override + public void writeInt(int value) { + ensureWritableBytes(4); + super.writeInt(value); + } + + @Override + public void writeLong(long value) { + ensureWritableBytes(8); + super.writeLong(value); + } + + @Override + public void writeBytes(byte[] src, int srcIndex, int length) { + ensureWritableBytes(length); + super.writeBytes(src, srcIndex, length); + } + + @Override + public void writeBytes(ChannelBuffer src, int srcIndex, int length) { + ensureWritableBytes(length); + super.writeBytes(src, srcIndex, length); + } + + @Override + public void writeBytes(ByteBuffer src) { + ensureWritableBytes(src.remaining()); + super.writeBytes(src); + } + + @Override + public void writePlaceholder(int length) { + ensureWritableBytes(length); + super.writePlaceholder(length); + } + + public ChannelBuffer duplicate() { + return new DuplicatedChannelBuffer(this); + } + + public ChannelBuffer copy(int index, int length) { + DynamicChannelBuffer copiedBuffer = new DynamicChannelBuffer(endianness, Math.max(length, 64)); + copiedBuffer.buffer = buffer.copy(); + if (copiedBuffer.buffer.capacity() == 0) { + copiedBuffer.buffer = ChannelBuffer.EMPTY_BUFFER; + } + return copiedBuffer; + } + + public ChannelBuffer slice(int index, int length) { + if (index == 0) { + return new TruncatedChannelBuffer(this, length); + } else { + return new SlicedChannelBuffer(this, index, length); + } + } + + public ByteBuffer toByteBuffer(int index, int length) { + return buffer.toByteBuffer(index, length); + } + + private void ensureWritableBytes(int requestedBytes) { + if (requestedBytes <= writableBytes()) { + return; + } + + int newCapacity; + if (capacity() == 0) { + newCapacity = initialCapacity; + } else { + newCapacity = capacity(); + } + int minNewCapacity = writerIndex() + requestedBytes; + while (newCapacity < minNewCapacity) { + newCapacity <<= 1; + } + + ChannelBuffer newBuffer = ChannelBuffers.buffer(endianness, newCapacity); + newBuffer.writeBytes(buffer, readerIndex(), readableBytes()); + buffer = newBuffer; + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/HeapChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/HeapChannelBuffer.java new file mode 100644 index 000000000000..b4da7b7edd2c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/HeapChannelBuffer.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public abstract class HeapChannelBuffer extends AbstractChannelBuffer { + + protected final byte[] array; + + public HeapChannelBuffer(int length) { + this(new byte[length], 0, 0); + } + + public HeapChannelBuffer(byte[] array) { + this(array, 0, array.length); + } + + protected HeapChannelBuffer(byte[] array, int readerIndex, int writerIndex) { + if (array == null) { + throw new NullPointerException("array"); + } + this.array = array; + setIndex(readerIndex, writerIndex); + } + + public int capacity() { + return array.length; + } + + public byte getByte(int index) { + return array[index]; + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + if (dst instanceof HeapChannelBuffer) { + getBytes(index, ((HeapChannelBuffer) dst).array, dstIndex, length); + } else { + dst.setBytes(dstIndex, array, index, length); + } + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + System.arraycopy(array, index, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + dst.put(array, index, Math.min(capacity() - index, dst.remaining())); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + out.write(array, index, length); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + return out.write(ByteBuffer.wrap(array, index, length)); + } + + public void setByte(int index, byte value) { + array[index] = value; + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + if (src instanceof HeapChannelBuffer) { + setBytes(index, ((HeapChannelBuffer) src).array, srcIndex, length); + } else { + src.getBytes(srcIndex, array, index, length); + } + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + System.arraycopy(src, srcIndex, array, index, length); + } + + public void setBytes(int index, ByteBuffer src) { + src.get(array, index, src.remaining()); + } + + public void setBytes(int index, InputStream in, int length) throws IOException { + while (length > 0) { + int readBytes = in.read(array, index, length); + if (readBytes < 0) { + throw new EOFException(); + } + index += readBytes; + length -= readBytes; + } + } + + public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(array, index, length); + while (length > 0) { + int readBytes = in.read(buf); + if (readBytes < 0) { + throw new EOFException(); + } else if (readBytes == 0) { + break; + } + } + + return buf.flip().remaining(); + } + + public ChannelBuffer slice(int index, int length) { + if (index == 0) { + if (length == array.length) { + return duplicate(); + } else { + return new TruncatedChannelBuffer(this, length); + } + } else { + return new SlicedChannelBuffer(this, index, length); + } + } + + public ByteBuffer toByteBuffer(int index, int length) { + return ByteBuffer.wrap(array, index, length); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBuffer.java new file mode 100644 index 000000000000..0d5ee7e27982 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBuffer.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.nio.ByteOrder; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public class LittleEndianHeapChannelBuffer extends HeapChannelBuffer { + + public LittleEndianHeapChannelBuffer(int length) { + super(length); + } + + public LittleEndianHeapChannelBuffer(byte[] array) { + super(array); + } + + private LittleEndianHeapChannelBuffer(byte[] array, int readerIndex, int writerIndex) { + super(array, readerIndex, writerIndex); + } + + public ByteOrder order() { + return ByteOrder.LITTLE_ENDIAN; + } + + public short getShort(int index) { + return (short) (array[index] & 0xFF | array[index+1] << 8); + } + + public int getMedium(int index) { + return (array[index ] & 0xff) << 0 | + (array[index+1] & 0xff) << 8 | + (array[index+2] & 0xff) << 16; + } + + public int getInt(int index) { + return (array[index ] & 0xff) << 0 | + (array[index+1] & 0xff) << 8 | + (array[index+2] & 0xff) << 16 | + (array[index+3] & 0xff) << 24; + } + + public long getLong(int index) { + return ((long) array[index] & 0xff) << 0 | + ((long) array[index+1] & 0xff) << 8 | + ((long) array[index+2] & 0xff) << 16 | + ((long) array[index+3] & 0xff) << 24 | + ((long) array[index+4] & 0xff) << 32 | + ((long) array[index+5] & 0xff) << 40 | + ((long) array[index+6] & 0xff) << 48 | + ((long) array[index+7] & 0xff) << 56; + } + + public void setShort(int index, short value) { + array[index ] = (byte) (value >>> 0); + array[index+1] = (byte) (value >>> 8); + } + + public void setMedium(int index, int value) { + array[index ] = (byte) (value >>> 0); + array[index+1] = (byte) (value >>> 8); + array[index+2] = (byte) (value >>> 16); + } + + public void setInt(int index, int value) { + array[index ] = (byte) (value >>> 0); + array[index+1] = (byte) (value >>> 8); + array[index+2] = (byte) (value >>> 16); + array[index+3] = (byte) (value >>> 24); + } + + public void setLong(int index, long value) { + array[index ] = (byte) (value >>> 0); + array[index+1] = (byte) (value >>> 8); + array[index+2] = (byte) (value >>> 16); + array[index+3] = (byte) (value >>> 24); + array[index+4] = (byte) (value >>> 32); + array[index+5] = (byte) (value >>> 40); + array[index+6] = (byte) (value >>> 48); + array[index+7] = (byte) (value >>> 56); + } + + public ChannelBuffer duplicate() { + return new LittleEndianHeapChannelBuffer(array, readerIndex(), writerIndex()); + } + + public ChannelBuffer copy(int index, int length) { + if (index < 0 || length < 0 || index + length > array.length) { + throw new IndexOutOfBoundsException(); + } + + byte[] copiedArray = new byte[length]; + System.arraycopy(array, index, copiedArray, 0, length); + return new LittleEndianHeapChannelBuffer(copiedArray); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/ReadOnlyChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/ReadOnlyChannelBuffer.java new file mode 100644 index 000000000000..9a1dedfd2b63 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/ReadOnlyChannelBuffer.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + +public class ReadOnlyChannelBuffer extends AbstractChannelBuffer implements WrappedChannelBuffer { + + private final ChannelBuffer buffer; + + public ReadOnlyChannelBuffer(ChannelBuffer buffer) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + this.buffer = buffer; + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + private ReadOnlyChannelBuffer(ReadOnlyChannelBuffer buffer) { + this.buffer = buffer.buffer; + setIndex(buffer.readerIndex(), buffer.writerIndex()); + } + + public ChannelBuffer unwrap() { + return buffer; + } + + public ByteOrder order() { + return buffer.order(); + } + + @Override + public void discardReadBytes() { + rejectModification(); + } + + public void setByte(int index, byte value) { + rejectModification(); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + rejectModification(); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + rejectModification(); + } + + public void setBytes(int index, ByteBuffer src) { + rejectModification(); + } + + public void setShort(int index, short value) { + rejectModification(); + } + + public void setMedium(int index, int value) { + rejectModification(); + } + + public void setInt(int index, int value) { + rejectModification(); + } + + public void setLong(int index, long value) { + rejectModification(); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + rejectModification(); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + rejectModification(); + return 0; + } + + protected void rejectModification() { + throw new UnsupportedOperationException("read-only"); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + return buffer.getBytes(index, out, length); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + buffer.getBytes(index, out, length); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + buffer.getBytes(index, dst); + } + + public ChannelBuffer duplicate() { + return new ReadOnlyChannelBuffer(this); + } + + public ChannelBuffer copy(int index, int length) { + return new ReadOnlyChannelBuffer(buffer.copy(index, length)); + } + + public ChannelBuffer slice(int index, int length) { + return new ReadOnlyChannelBuffer(buffer.slice(index, length)); + } + + public byte getByte(int index) { + return buffer.getByte(index); + } + + public short getShort(int index) { + return buffer.getShort(index); + } + + public int getMedium(int index) { + return buffer.getMedium(index); + } + + public int getInt(int index) { + return buffer.getInt(index); + } + + public long getLong(int index) { + return buffer.getLong(index); + } + + public ByteBuffer toByteBuffer(int index, int length) { + return buffer.toByteBuffer(index, length).asReadOnlyBuffer(); + } + + public int capacity() { + return buffer.capacity(); + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/SlicedChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/SlicedChannelBuffer.java new file mode 100644 index 000000000000..795e9191138f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/SlicedChannelBuffer.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SlicedChannelBuffer extends AbstractChannelBuffer implements WrappedChannelBuffer { + + private final ChannelBuffer buffer; + private final int adjustment; + private final int length; + + public SlicedChannelBuffer(ChannelBuffer buffer, int index, int length) { + if (index < 0 || index > buffer.capacity()) { + throw new IndexOutOfBoundsException(); + } + + if (index + length > buffer.capacity()) { + throw new IndexOutOfBoundsException(); + } + + this.buffer = buffer; + adjustment = index; + this.length = length; + writerIndex(length); + } + + public ChannelBuffer unwrap() { + return buffer; + } + + public ByteOrder order() { + return buffer.order(); + } + + public int capacity() { + return length; + } + + public byte getByte(int index) { + checkIndex(index); + return buffer.getByte(index + adjustment); + } + + public short getShort(int index) { + checkIndex(index, 2); + return buffer.getShort(index + adjustment); + } + + public int getMedium(int index) { + checkIndex(index, 3); + return buffer.getMedium(index + adjustment); + } + + public int getInt(int index) { + checkIndex(index, 4); + return buffer.getInt(index + adjustment); + } + + public long getLong(int index) { + checkIndex(index, 8); + return buffer.getLong(index + adjustment); + } + + public ChannelBuffer duplicate() { + return new SlicedChannelBuffer(buffer, adjustment, length); + } + + public ChannelBuffer copy(int index, int length) { + return buffer.copy(index + adjustment, length); + } + + public ChannelBuffer slice(int index, int length) { + checkIndex(index, length); + return new SlicedChannelBuffer(buffer, index + adjustment, length); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index + adjustment, dst, dstIndex, length); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index + adjustment, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + checkIndex(index, dst.remaining()); + buffer.getBytes(index + adjustment, dst); + } + + public void setByte(int index, byte value) { + checkIndex(index); + buffer.setByte(index + adjustment, value); + } + + public void setShort(int index, short value) { + checkIndex(index, 2); + buffer.setShort(index + adjustment, value); + } + + public void setMedium(int index, int value) { + checkIndex(index, 3); + buffer.setMedium(index + adjustment, value); + } + + public void setInt(int index, int value) { + checkIndex(index, 4); + buffer.setInt(index + adjustment, value); + } + + public void setLong(int index, long value) { + checkIndex(index, 8); + buffer.setLong(index + adjustment, value); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + checkIndex(index, length); + buffer.setBytes(index + adjustment, src, srcIndex, length); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + checkIndex(index, length); + buffer.setBytes(index + adjustment, src, srcIndex, length); + } + + public void setBytes(int index, ByteBuffer src) { + checkIndex(index, src.remaining()); + buffer.setBytes(index + adjustment, src); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + checkIndex(index, length); + buffer.getBytes(index + adjustment, out, length); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + checkIndex(index, length); + return buffer.getBytes(index + adjustment, out, length); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + checkIndex(index, length); + buffer.setBytes(index + adjustment, in, length); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + checkIndex(index, length); + return buffer.setBytes(index + adjustment, in, length); + } + + public ByteBuffer toByteBuffer(int index, int length) { + checkIndex(index, length); + return buffer.toByteBuffer(index + adjustment, length); + } + + private void checkIndex(int index) { + if (index < 0 || index >= capacity()) { + throw new IndexOutOfBoundsException(); + } + } + + private void checkIndex(int startIndex, int length) { + if (length < 0) { + throw new IllegalArgumentException( + "length is negative: " + length); + } + if (startIndex < 0) { + throw new IndexOutOfBoundsException(); + } + if (startIndex + length > capacity()) { + throw new IndexOutOfBoundsException(); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/TruncatedChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/TruncatedChannelBuffer.java new file mode 100644 index 000000000000..a6cc3f6468b0 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/TruncatedChannelBuffer.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class TruncatedChannelBuffer extends AbstractChannelBuffer implements WrappedChannelBuffer { + + private final ChannelBuffer buffer; + private final int length; + + public TruncatedChannelBuffer(ChannelBuffer buffer, int length) { + if (length > buffer.capacity()) { + throw new IndexOutOfBoundsException(); + } + + this.buffer = buffer; + this.length = length; + writerIndex(length); + } + + public ChannelBuffer unwrap() { + return buffer; + } + + public ByteOrder order() { + return buffer.order(); + } + + public int capacity() { + return length; + } + + public byte getByte(int index) { + checkIndex(index); + return buffer.getByte(index); + } + + public short getShort(int index) { + checkIndex(index, 2); + return buffer.getShort(index); + } + + public int getMedium(int index) { + checkIndex(index, 3); + return buffer.getMedium(index); + } + + public int getInt(int index) { + checkIndex(index, 4); + return buffer.getInt(index); + } + + public long getLong(int index) { + checkIndex(index, 8); + return buffer.getLong(index); + } + + public ChannelBuffer duplicate() { + return new TruncatedChannelBuffer(buffer, length); + } + + public ChannelBuffer copy(int index, int length) { + return buffer.copy(index, length); + } + + public ChannelBuffer slice(int index, int length) { + checkIndex(index, length); + return buffer.slice(index, length); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ByteBuffer dst) { + checkIndex(index, dst.remaining()); + buffer.getBytes(index, dst); + } + + public void setByte(int index, byte value) { + checkIndex(index); + buffer.setByte(index, value); + } + + public void setShort(int index, short value) { + checkIndex(index, 2); + buffer.setShort(index, value); + } + + public void setMedium(int index, int value) { + checkIndex(index, 3); + buffer.setMedium(index, value); + } + + public void setInt(int index, int value) { + checkIndex(index, 4); + buffer.setInt(index, value); + } + + public void setLong(int index, long value) { + checkIndex(index, 8); + buffer.setLong(index, value); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + checkIndex(index, length); + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + checkIndex(index, length); + buffer.setBytes(index, src, srcIndex, length); + } + + public void setBytes(int index, ByteBuffer src) { + checkIndex(index, src.remaining()); + buffer.setBytes(index, src); + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + checkIndex(index, length); + buffer.getBytes(index, out, length); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + checkIndex(index, length); + return buffer.getBytes(index, out, length); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + checkIndex(index, length); + buffer.setBytes(index, in, length); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + checkIndex(index, length); + return buffer.setBytes(index, in, length); + } + + public ByteBuffer toByteBuffer(int index, int length) { + checkIndex(index, length); + return buffer.toByteBuffer(index, length); + } + + private void checkIndex(int index) { + if (index < 0 || index >= capacity()) { + throw new IndexOutOfBoundsException(); + } + } + + private void checkIndex(int index, int length) { + if (length < 0) { + throw new IllegalArgumentException( + "length is negative: " + length); + } + if (index + length > capacity()) { + throw new IndexOutOfBoundsException(); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/buffer/WrappedChannelBuffer.java b/src/main/java/net/gleamynode/netty/buffer/WrappedChannelBuffer.java new file mode 100644 index 000000000000..b5e37e18a297 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/WrappedChannelBuffer.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public interface WrappedChannelBuffer extends ChannelBuffer { + ChannelBuffer unwrap(); +} diff --git a/src/main/java/net/gleamynode/netty/buffer/package-info.java b/src/main/java/net/gleamynode/netty/buffer/package-info.java new file mode 100644 index 000000000000..8d4847f4576c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/buffer/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Abstraction of a byte-level buffer - the fundamental data structure + * to represent low-level binary and text messages. + * + * @apiviz.landmark + * @apiviz.exclude ^java\.io\.[^\.]+Stream$ + */ +package net.gleamynode.netty.buffer; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/AbstractChannel.java b/src/main/java/net/gleamynode/netty/channel/AbstractChannel.java new file mode 100644 index 000000000000..8d2c5a6bb600 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/AbstractChannel.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.gleamynode.netty.util.TimeBasedUuidGenerator; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public abstract class AbstractChannel implements Channel, Comparable { + + private final UUID id = TimeBasedUuidGenerator.generate(); + private final Channel parent; + private final ChannelFactory factory; + private final ChannelPipeline pipeline; + private final ChannelFuture succeededFuture = new SucceededChannelFuture(this); + + private final AtomicBoolean closed = new AtomicBoolean(); + private volatile int interestOps = OP_READ; + + /** Cache for the string representation of this channel */ + private String strVal; + + protected AbstractChannel( + Channel parent, ChannelFactory factory, + ChannelPipeline pipeline, ChannelSink sink) { + + this.parent = parent; + this.factory = factory; + this.pipeline = pipeline; + pipeline.attach(this, sink); + } + + public final UUID getId() { + return id; + } + + public Channel getParent() { + return parent; + } + + public ChannelFactory getFactory() { + return factory; + } + + public ChannelPipeline getPipeline() { + return pipeline; + } + + protected ChannelFuture getSucceededFuture() { + return succeededFuture; + } + + protected ChannelFuture getUnsupportedOperationFuture() { + return new FailedChannelFuture(this, new UnsupportedOperationException()); + } + + @Override + public final int hashCode() { + return System.identityHashCode(this); + } + + @Override + public final boolean equals(Object o) { + return this == o; + } + + public int compareTo(Channel o) { + return System.identityHashCode(this) - System.identityHashCode(o); + } + + public boolean isOpen() { + return !closed.get(); + } + + protected boolean setClosed() { + return closed.compareAndSet(false, true); + } + + public ChannelFuture bind(SocketAddress localAddress) { + return Channels.bind(this, localAddress); + } + + public ChannelFuture close() { + return Channels.close(this); + } + + public ChannelFuture connect(SocketAddress remoteAddress) { + return Channels.connect(this, remoteAddress); + } + + public ChannelFuture disconnect() { + return Channels.disconnect(this); + } + + public int getInterestOps() { + return interestOps; + } + + public ChannelFuture setInterestOps(int interestOps) { + return Channels.setInterestOps(this, interestOps); + } + + protected void setInterestOpsNow(int interestOps) { + this.interestOps = interestOps; + } + + public boolean isReadable() { + return (getInterestOps() & OP_READ) != 0; + } + + public boolean isWritable() { + return (getInterestOps() & OP_WRITE) == 0; + } + + public ChannelFuture setReadable(boolean readable) { + if (readable) { + return setInterestOps(getInterestOps() | OP_READ); + } else { + return setInterestOps(getInterestOps() & ~OP_READ); + } + } + + public ChannelFuture write(Object message) { + return Channels.write(this, message); + } + + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + return Channels.write(this, message, remoteAddress); + } + + @Override + public String toString() { + if (strVal != null) { + return strVal; + } + + StringBuilder buf = new StringBuilder(128); + buf.append(getClass().getSimpleName()); + buf.append("(id: "); + buf.append(id.toString()); + + if (isConnected()) { + buf.append(", "); + if (getParent() == null) { + buf.append(getLocalAddress()); + buf.append(" => "); + buf.append(getRemoteAddress()); + } else { + buf.append(getRemoteAddress()); + buf.append(" => "); + buf.append(getLocalAddress()); + } + } else if (isBound()) { + buf.append(", "); + buf.append(getLocalAddress()); + } + + buf.append(')'); + + return strVal = buf.toString(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/AbstractChannelSink.java b/src/main/java/net/gleamynode/netty/channel/AbstractChannelSink.java new file mode 100644 index 000000000000..98c8b60f9aae --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/AbstractChannelSink.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import static net.gleamynode.netty.channel.Channels.*; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public abstract class AbstractChannelSink implements ChannelSink { + + public void exceptionCaught(ChannelPipeline pipeline, + ChannelEvent event, ChannelPipelineException cause) throws Exception { + Throwable actualCause = cause.getCause(); + if (actualCause == null) { + actualCause = cause; + } + + fireExceptionCaught(event.getChannel(), actualCause); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/AbstractServerChannel.java b/src/main/java/net/gleamynode/netty/channel/AbstractServerChannel.java new file mode 100644 index 000000000000..e5c5487f8f8d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/AbstractServerChannel.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; + + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public abstract class AbstractServerChannel extends AbstractChannel { + + protected AbstractServerChannel( + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink) { + super(null, factory, pipeline, sink); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress) { + return getUnsupportedOperationFuture(); + } + + @Override + public ChannelFuture disconnect() { + return getUnsupportedOperationFuture(); + } + + @Override + public int getInterestOps() { + return OP_NONE; + } + + @Override + public ChannelFuture setInterestOps(int interestOps) { + return getUnsupportedOperationFuture(); + } + + @Override + protected void setInterestOpsNow(int interestOps) { + // Ignore. + } + + @Override + public ChannelFuture write(Object message) { + return getUnsupportedOperationFuture(); + } + + @Override + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + return getUnsupportedOperationFuture(); + } + +} diff --git a/src/main/java/net/gleamynode/netty/channel/Channel.java b/src/main/java/net/gleamynode/netty/channel/Channel.java new file mode 100644 index 000000000000..3844ae8befbc --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/Channel.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; +import java.util.UUID; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.composedOf net.gleamynode.netty.channel.ChannelConfig + * @apiviz.composedOf net.gleamynode.netty.channel.ChannelPipeline + */ +public interface Channel { + static int OP_NONE = 0; + static int OP_READ = 1; + static int OP_WRITE = 4; + static int OP_READ_WRITE = OP_READ | OP_WRITE; + + UUID getId(); + ChannelFactory getFactory(); + Channel getParent(); + ChannelConfig getConfig(); + ChannelPipeline getPipeline(); + + boolean isOpen(); + boolean isBound(); + boolean isConnected(); + + SocketAddress getLocalAddress(); + SocketAddress getRemoteAddress(); + + ChannelFuture write(Object message); + ChannelFuture write(Object message, SocketAddress remoteAddress); + + ChannelFuture bind(SocketAddress localAddress); + ChannelFuture connect(SocketAddress remoteAddress); + ChannelFuture disconnect(); + ChannelFuture close(); + + int getInterestOps(); + boolean isReadable(); + boolean isWritable(); + ChannelFuture setInterestOps(int interestOps); + ChannelFuture setReadable(boolean readable); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/ChannelConfig.java new file mode 100644 index 000000000000..7716cebc0308 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelConfig.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.Map; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.ChannelPipelineFactory + */ +public interface ChannelConfig { + void setOptions(Map options); + ChannelPipelineFactory getPipelineFactory(); + void setPipelineFactory(ChannelPipelineFactory pipelineFactory); + int getConnectTimeoutMillis(); + void setConnectTimeoutMillis(int connectTimeoutMillis); + int getWriteTimeoutMillis(); + void setWriteTimeoutMillis(int writeTimeoutMillis); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelDownstreamHandler.java b/src/main/java/net/gleamynode/netty/channel/ChannelDownstreamHandler.java new file mode 100644 index 000000000000..de2c20cec5af --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelDownstreamHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public interface ChannelDownstreamHandler extends ChannelHandler { + void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception; +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelEvent.java b/src/main/java/net/gleamynode/netty/channel/ChannelEvent.java new file mode 100644 index 000000000000..0fbdc2ddaba7 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.composedOf net.gleamynode.netty.channel.ChannelFuture + */ +public interface ChannelEvent { + Channel getChannel(); + ChannelFuture getFuture(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelException.java b/src/main/java/net/gleamynode/netty/channel/ChannelException.java new file mode 100644 index 000000000000..51bd88e5a6d4 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.hidden + */ +public class ChannelException extends RuntimeException { + + private static final long serialVersionUID = 2908618315971075004L; + + public ChannelException() { + super(); + } + + public ChannelException(String message, Throwable cause) { + super(message, cause); + } + + public ChannelException(String message) { + super(message); + } + + public ChannelException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/ChannelFactory.java new file mode 100644 index 000000000000..325f7ca6603a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.has net.gleamynode.netty.channel.Channel oneway - - creates + */ +public interface ChannelFactory { + Channel newChannel(ChannelPipeline pipeline); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelFuture.java b/src/main/java/net/gleamynode/netty/channel/ChannelFuture.java new file mode 100644 index 000000000000..f29335c94ec8 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelFuture.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.concurrent.TimeUnit; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.owns net.gleamynode.netty.channel.ChannelFutureListener - - notifies + */ +public interface ChannelFuture { + Channel getChannel(); + + boolean isDone(); + boolean isCancelled(); + boolean isSuccess(); + Throwable getCause(); + + boolean cancel(); + + void setSuccess(); + void setFailure(Throwable cause); + + void addListener(ChannelFutureListener listener); + void removeListener(ChannelFutureListener listener); + + ChannelFuture await() throws InterruptedException; + ChannelFuture awaitUninterruptibly(); + boolean await(long timeout, TimeUnit unit) throws InterruptedException; + boolean await(long timeoutMillis) throws InterruptedException; + boolean awaitUninterruptibly(long timeout, TimeUnit unit); + boolean awaitUninterruptibly(long timeoutMillis); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelFutureListener.java b/src/main/java/net/gleamynode/netty/channel/ChannelFutureListener.java new file mode 100644 index 000000000000..27d22605b156 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelFutureListener.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.EventListener; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public interface ChannelFutureListener extends EventListener { + /** + * An {@link ChannelFutureListener} that closes the {@link Channel} which is + * associated with the specified {@link ChannelFuture}. + */ + static ChannelFutureListener CLOSE = new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + future.getChannel().close(); + } + }; + + /** + * Invoked when the operation associated with the {@link ChannelFuture} + * has been completed. + * + * @param future The source {@link ChannelFuture} which called this + * callback. + */ + void operationComplete(ChannelFuture future) throws Exception; +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelHandler.java b/src/main/java/net/gleamynode/netty/channel/ChannelHandler.java new file mode 100644 index 000000000000..72c53ad6a041 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelHandler.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public interface ChannelHandler { + // Thsi is a tag interface. +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelHandlerContext.java b/src/main/java/net/gleamynode/netty/channel/ChannelHandlerContext.java new file mode 100644 index 000000000000..1c8376efb4aa --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelHandlerContext.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.owns net.gleamynode.netty.channel.ChannelHandler + */ +public interface ChannelHandlerContext { + ChannelPipeline getPipeline(); + + String getName(); + ChannelHandler getHandler(); + boolean canHandleUpstream(); + boolean canHandleDownstream(); + + void sendUpstream(ChannelEvent e); + void sendDownstream(ChannelEvent e); +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelPipeline.java b/src/main/java/net/gleamynode/netty/channel/ChannelPipeline.java new file mode 100644 index 000000000000..00ff006691c8 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelPipeline.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.Map; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.composedOf net.gleamynode.netty.channel.ChannelHandlerContext + * @apiviz.owns net.gleamynode.netty.channel.ChannelHandler + * @apiviz.uses net.gleamynode.netty.channel.ChannelSink - - sends events downstream + */ +public interface ChannelPipeline { + void addFirst (String name, ChannelHandler handler); + void addLast (String name, ChannelHandler handler); + void addBefore(String baseName, String name, ChannelHandler handler); + void addAfter (String baseName, String name, ChannelHandler handler); + + void remove(ChannelHandler handler); + ChannelHandler remove(String name); + T remove(Class handlerType); + ChannelHandler removeFirst(); + ChannelHandler removeLast(); + + void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler); + ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler); + T replace(Class oldHandlerType, String newName, ChannelHandler newHandler); + + ChannelHandler getFirst(); + ChannelHandler getLast(); + + ChannelHandler get(String name); + T get(Class handlerType); + + ChannelHandlerContext getContext(ChannelHandler handler); + ChannelHandlerContext getContext(String name); + ChannelHandlerContext getContext(Class handlerType); + + void sendUpstream(ChannelEvent e); + void sendDownstream(ChannelEvent e); + + Channel getChannel(); + ChannelSink getSink(); + void attach(Channel channel, ChannelSink sink); + + Map toMap(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelPipelineCoverage.java b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineCoverage.java new file mode 100644 index 000000000000..f70551ad780a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineCoverage.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +@Inherited +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ChannelPipelineCoverage { + public static final String ALL = "all"; + public static final String ONE = "one"; + + String value(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelPipelineException.java b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineException.java new file mode 100644 index 000000000000..5db337dd17b8 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.hidden + */ +public class ChannelPipelineException extends ChannelException { + + private static final long serialVersionUID = 3379174210419885980L; + + public ChannelPipelineException() { + super(); + } + + public ChannelPipelineException(String message, Throwable cause) { + super(message, cause); + } + + public ChannelPipelineException(String message) { + super(message); + } + + public ChannelPipelineException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelPipelineFactory.java b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineFactory.java new file mode 100644 index 000000000000..0bcf84ba10d2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelPipelineFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.ChannelPipeline oneway - - creates + */ +public interface ChannelPipelineFactory { + ChannelPipeline getPipeline() throws Exception; +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelSink.java b/src/main/java/net/gleamynode/netty/channel/ChannelSink.java new file mode 100644 index 000000000000..6931b7317961 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelSink.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.uses net.gleamynode.netty.channel.ChannelPipeline - - sends events upstream + */ +public interface ChannelSink { + void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception; + void exceptionCaught(ChannelPipeline pipeline, ChannelEvent e, ChannelPipelineException cause) throws Exception; +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelState.java b/src/main/java/net/gleamynode/netty/channel/ChannelState.java new file mode 100644 index 000000000000..38fafebdc18d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelState.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public enum ChannelState { + OPEN, + BOUND, + CONNECTED, + INTEREST_OPS, +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelStateEvent.java b/src/main/java/net/gleamynode/netty/channel/ChannelStateEvent.java new file mode 100644 index 000000000000..ac6a582fbb43 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelStateEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.ChannelState + */ +public interface ChannelStateEvent extends ChannelEvent { + ChannelState getState(); + Object getValue(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChannelUpstreamHandler.java b/src/main/java/net/gleamynode/netty/channel/ChannelUpstreamHandler.java new file mode 100644 index 000000000000..740634c4b85a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChannelUpstreamHandler.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public interface ChannelUpstreamHandler extends ChannelHandler { + void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception; +} diff --git a/src/main/java/net/gleamynode/netty/channel/Channels.java b/src/main/java/net/gleamynode/netty/channel/Channels.java new file mode 100644 index 000000000000..e6b9091dd89e --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/Channels.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; +import java.util.Map; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + */ +public class Channels { + + // pipeline factory methods + + public static ChannelPipeline pipeline() { + return new DefaultChannelPipeline(); + } + + public static ChannelPipeline pipeline(ChannelPipeline pipeline) { + ChannelPipeline newPipeline = pipeline(); + for (Map.Entry e: pipeline.toMap().entrySet()) { + newPipeline.addLast(e.getKey(), e.getValue()); + } + return newPipeline; + } + + public static ChannelPipelineFactory pipelineFactory( + final ChannelPipeline pipeline) { + return new ChannelPipelineFactory() { + public ChannelPipeline getPipeline() { + return pipeline(pipeline); + } + }; + } + + // future factory methods + + public static ChannelFuture future(Channel channel) { + return future(channel, false); + } + + public static ChannelFuture future(Channel channel, boolean cancellable) { + return new DefaultChannelFuture(channel, cancellable); + } + + public static ChannelFuture succeededFuture(Channel channel) { + if (channel instanceof AbstractChannel) { + return ((AbstractChannel) channel).getSucceededFuture(); + } else { + return new SucceededChannelFuture(channel); + } + } + + public static ChannelFuture failedFuture(Channel channel, Throwable cause) { + return new FailedChannelFuture(channel, cause); + } + + // event factory methods + + public static MessageEvent messageEvent(Channel channel, ChannelFuture future, Object message) { + return messageEvent(channel, future, message, null); + } + + public static MessageEvent messageEvent(Channel channel, ChannelFuture future, Object message, SocketAddress remoteAddress) { + return new DefaultMessageEvent(channel, future, message, remoteAddress); + } + + // event emission methods + + public static void fireChannelOpen(Channel channel) { + if (channel.getParent() != null) { + fireChildChannelStateChanged(channel.getParent(), channel); + } + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.OPEN, Boolean.TRUE)); + } + + public static void fireChannelOpen( + ChannelHandlerContext ctx, Channel channel) { + + ctx.sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.OPEN, Boolean.TRUE)); + } + + public static void fireChannelBound(Channel channel, SocketAddress localAddress) { + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.BOUND, localAddress)); + } + + public static void fireChannelBound( + ChannelHandlerContext ctx, Channel channel, SocketAddress localAddress) { + + ctx.sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.BOUND, localAddress)); + } + + public static void fireChannelConnected(Channel channel, SocketAddress remoteAddress) { + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.CONNECTED, remoteAddress)); + } + + public static void fireChannelConnected( + ChannelHandlerContext ctx, Channel channel, SocketAddress remoteAddress) { + + ctx.sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.CONNECTED, remoteAddress)); + } + + public static void fireMessageReceived(Channel channel, Object message) { + fireMessageReceived(channel, message, null); + } + + public static void fireMessageReceived(Channel channel, Object message, SocketAddress remoteAddress) { + channel.getPipeline().sendUpstream( + new DefaultMessageEvent( + channel, succeededFuture(channel), + message, remoteAddress)); + } + + public static void fireMessageReceived( + ChannelHandlerContext ctx, Channel channel, Object message) { + ctx.sendUpstream(new DefaultMessageEvent( + channel, succeededFuture(channel), message, null)); + } + + public static void fireMessageReceived( + ChannelHandlerContext ctx, Channel channel, + Object message, SocketAddress remoteAddress) { + ctx.sendUpstream(new DefaultMessageEvent( + channel, succeededFuture(channel), message, remoteAddress)); + } + + public static void fireChannelInterestChanged(Channel channel, int interestOps) { + validateInterestOps(interestOps); + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.INTEREST_OPS, Integer.valueOf(interestOps))); + } + + public static void fireChannelInterestChanged( + ChannelHandlerContext ctx, Channel channel, int interestOps) { + + validateInterestOps(interestOps); + ctx.sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.INTEREST_OPS, Integer.valueOf(interestOps))); + } + + public static void fireChannelDisconnected(Channel channel) { + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.CONNECTED, null)); + } + + public static void fireChannelDisconnected( + ChannelHandlerContext ctx, Channel channel) { + ctx.sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.CONNECTED, null)); + } + + public static void fireChannelUnbound(Channel channel) { + channel.getPipeline().sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), ChannelState.BOUND, null)); + } + + public static void fireChannelUnbound( + ChannelHandlerContext ctx, Channel channel) { + + ctx.sendUpstream(new DefaultChannelStateEvent( + channel, succeededFuture(channel), ChannelState.BOUND, null)); + } + + public static void fireChannelClosed(Channel channel) { + channel.getPipeline().sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.OPEN, Boolean.FALSE)); + if (channel.getParent() != null) { + fireChildChannelStateChanged(channel.getParent(), channel); + } + } + + public static void fireChannelClosed( + ChannelHandlerContext ctx, Channel channel) { + ctx.sendUpstream( + new DefaultChannelStateEvent( + channel, succeededFuture(channel), + ChannelState.OPEN, Boolean.FALSE)); + } + + public static void fireExceptionCaught(Channel channel, Throwable cause) { + channel.getPipeline().sendUpstream( + new DefaultExceptionEvent( + channel, succeededFuture(channel), cause)); + } + + public static void fireExceptionCaught( + ChannelHandlerContext ctx, Channel channel, Throwable cause) { + ctx.sendUpstream(new DefaultExceptionEvent( + channel, succeededFuture(channel), cause)); + } + + private static void fireChildChannelStateChanged( + Channel channel, Channel childChannel) { + channel.getPipeline().sendUpstream( + new DefaultChildChannelStateEvent( + channel, succeededFuture(channel), childChannel)); + } + + public static ChannelFuture bind(Channel channel, SocketAddress localAddress) { + ChannelFuture future = future(channel); + channel.getPipeline().sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.BOUND, localAddress)); + return future; + } + + public static void bind( + ChannelHandlerContext ctx, Channel channel, + ChannelFuture future, SocketAddress localAddress) { + + ctx.sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.BOUND, localAddress)); + } + + public static ChannelFuture connect(Channel channel, SocketAddress remoteAddress) { + ChannelFuture future = future(channel, true); + channel.getPipeline().sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.CONNECTED, remoteAddress)); + return future; + } + + public static void connect( + ChannelHandlerContext ctx, Channel channel, + ChannelFuture future, SocketAddress remoteAddress) { + + ctx.sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.CONNECTED, remoteAddress)); + } + + public static ChannelFuture write(Channel channel, Object message) { + return write(channel, message, null); + } + + public static void write( + ChannelHandlerContext ctx, Channel channel, + ChannelFuture future, Object message) { + write(ctx, channel, future, message, null); + } + + public static ChannelFuture write(Channel channel, Object message, SocketAddress remoteAddress) { + ChannelFuture future = future(channel); + channel.getPipeline().sendDownstream( + new DefaultMessageEvent(channel, future, message, remoteAddress)); + return future; + } + + public static void write( + ChannelHandlerContext ctx, Channel channel, + ChannelFuture future, Object message, SocketAddress remoteAddress) { + ctx.sendDownstream( + new DefaultMessageEvent(channel, future, message, remoteAddress)); + } + + public static ChannelFuture setInterestOps(Channel channel, int interestOps) { + validateInterestOps(interestOps); + validateDownstreamInterestOps(channel, interestOps); + + ChannelFuture future = future(channel); + channel.getPipeline().sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.INTEREST_OPS, Integer.valueOf(interestOps))); + return future; + } + + public static void setInterestOps( + ChannelHandlerContext ctx, Channel channel, + ChannelFuture future, int interestOps) { + validateInterestOps(interestOps); + validateDownstreamInterestOps(channel, interestOps); + + ctx.sendDownstream( + new DefaultChannelStateEvent( + channel, future, ChannelState.INTEREST_OPS, + Integer.valueOf(interestOps))); + } + + public static ChannelFuture disconnect(Channel channel) { + ChannelFuture future = future(channel); + channel.getPipeline().sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.CONNECTED, null)); + return future; + } + + public static void disconnect( + ChannelHandlerContext ctx, Channel channel, ChannelFuture future) { + ctx.sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.CONNECTED, null)); + } + + public static ChannelFuture close(Channel channel) { + ChannelFuture future = future(channel); + channel.getPipeline().sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.OPEN, Boolean.FALSE)); + return future; + } + + public static void close( + ChannelHandlerContext ctx, Channel channel, ChannelFuture future) { + ctx.sendDownstream(new DefaultChannelStateEvent( + channel, future, ChannelState.OPEN, Boolean.FALSE)); + } + + private static void validateInterestOps(int interestOps) { + switch (interestOps) { + case Channel.OP_NONE: + case Channel.OP_READ: + case Channel.OP_WRITE: + case Channel.OP_READ_WRITE: + break; + default: + throw new IllegalArgumentException( + "Invalid interestOps: " + interestOps); + } + } + + private static void validateDownstreamInterestOps(Channel channel, int interestOps) { + if (((channel.getInterestOps() ^ interestOps) & Channel.OP_WRITE) != 0) { + throw new IllegalArgumentException("OP_WRITE can't be modified by user."); + } + } + + private Channels() { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/ChildChannelStateEvent.java b/src/main/java/net/gleamynode/netty/channel/ChildChannelStateEvent.java new file mode 100644 index 000000000000..8ff31266bc92 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ChildChannelStateEvent.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public interface ChildChannelStateEvent extends ChannelEvent { + Channel getChildChannel(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/CompleteChannelFuture.java b/src/main/java/net/gleamynode/netty/channel/CompleteChannelFuture.java new file mode 100644 index 000000000000..06a7dbc37fad --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/CompleteChannelFuture.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.concurrent.TimeUnit; + +import net.gleamynode.netty.logging.Logger; + +public abstract class CompleteChannelFuture implements ChannelFuture { + + private static final Logger logger = + Logger.getLogger(CompleteChannelFuture.class); + + private final Channel channel; + + protected CompleteChannelFuture(Channel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + public void addListener(ChannelFutureListener listener) { + try { + listener.operationComplete(this); + } catch (Throwable t) { + logger.warn( + "An exception was thrown by " + + ChannelFutureListener.class.getSimpleName() + ".", t); + } + } + + public void removeListener(ChannelFutureListener listener) { + // NOOP + } + + public ChannelFuture await() throws InterruptedException { + return this; + } + + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return true; + } + + public boolean await(long timeoutMillis) throws InterruptedException { + return true; + } + + public ChannelFuture awaitUninterruptibly() { + return this; + } + + public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { + return true; + } + + public boolean awaitUninterruptibly(long timeoutMillis) { + return true; + } + + public Channel getChannel() { + return channel; + } + + public boolean isDone() { + return true; + } + + public void setFailure(Throwable cause) { + // Unused + } + + public void setSuccess() { + // Unused + } + + public boolean cancel() { + return false; + } + + public boolean isCancelled() { + return false; + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultChannelEvent.java b/src/main/java/net/gleamynode/netty/channel/DefaultChannelEvent.java new file mode 100644 index 000000000000..da5f40fc6505 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultChannelEvent.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +public class DefaultChannelEvent implements ChannelEvent { + + private final Channel channel; + private final ChannelFuture future; + + public DefaultChannelEvent(Channel channel, ChannelFuture future) { + if (channel == null) { + throw new NullPointerException("channel"); + } + if (future == null) { + throw new NullPointerException("future"); + } + this.channel = channel; + this.future = future; + } + + public final Channel getChannel() { + return channel; + } + + public final ChannelFuture getFuture() { + return future; + } + + @Override + public String toString() { + return channel.toString(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultChannelFuture.java b/src/main/java/net/gleamynode/netty/channel/DefaultChannelFuture.java new file mode 100644 index 000000000000..3394e49e7a9b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultChannelFuture.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import net.gleamynode.netty.logging.Logger; + + +public class DefaultChannelFuture implements ChannelFuture { + + private static final Logger logger = + Logger.getLogger(DefaultChannelFuture.class); + private static final int DEAD_LOCK_CHECK_INTERVAL = 5000; + private static final Throwable CANCELLED = new Throwable(); + + private final Channel channel; + private final boolean cancellable; + + private ChannelFutureListener firstListener; + private List otherListeners; + private boolean done; + private Throwable cause; + private int waiters; + + public DefaultChannelFuture(Channel channel, boolean cancellable) { + this.channel = channel; + this.cancellable = cancellable; + } + + public Channel getChannel() { + return channel; + } + + public synchronized boolean isDone() { + return done; + } + + public synchronized boolean isSuccess() { + return cause == null; + } + + public synchronized Throwable getCause() { + if (cause != CANCELLED) { + return cause; + } else { + return null; + } + } + + public synchronized boolean isCancelled() { + return cause == CANCELLED; + } + + public void addListener(ChannelFutureListener listener) { + if (listener == null) { + throw new NullPointerException("listener"); + } + + boolean notifyNow = false; + synchronized (this) { + if (done) { + notifyNow = true; + } else { + if (firstListener == null) { + firstListener = listener; + } else { + if (otherListeners == null) { + otherListeners = new ArrayList(1); + } + otherListeners.add(listener); + } + } + } + + if (notifyNow) { + notifyListener(listener); + } + } + + public void removeListener(ChannelFutureListener listener) { + if (listener == null) { + throw new NullPointerException("listener"); + } + + synchronized (this) { + if (!done) { + if (listener == firstListener) { + if (otherListeners != null && !otherListeners.isEmpty()) { + firstListener = otherListeners.remove(0); + } else { + firstListener = null; + } + } else if (otherListeners != null) { + otherListeners.remove(listener); + } + } + } + } + + public ChannelFuture await() throws InterruptedException { + synchronized (this) { + while (!done) { + waiters++; + try { + this.wait(DEAD_LOCK_CHECK_INTERVAL); + checkDeadLock(); + } finally { + waiters--; + } + } + } + return this; + } + + public boolean await(long timeout, TimeUnit unit) + throws InterruptedException { + return await(unit.toMillis(timeout)); + } + + public boolean await(long timeoutMillis) throws InterruptedException { + return await0(timeoutMillis, true); + } + + public ChannelFuture awaitUninterruptibly() { + synchronized (this) { + while (!done) { + waiters++; + try { + this.wait(DEAD_LOCK_CHECK_INTERVAL); + } catch (InterruptedException e) { + // Ignore. + } finally { + waiters--; + if (!done) { + checkDeadLock(); + } + } + } + } + + return this; + } + + public boolean awaitUninterruptibly(long timeout, TimeUnit unit) { + return awaitUninterruptibly(unit.toMillis(timeout)); + } + + public boolean awaitUninterruptibly(long timeoutMillis) { + try { + return await0(timeoutMillis, false); + } catch (InterruptedException e) { + throw new InternalError(); + } + } + + private boolean await0(long timeoutMillis, boolean interruptable) throws InterruptedException { + long startTime = timeoutMillis <= 0 ? 0 : System.currentTimeMillis(); + long waitTime = timeoutMillis; + + synchronized (this) { + if (done) { + return done; + } else if (waitTime <= 0) { + return done; + } + + waiters++; + try { + for (;;) { + try { + this.wait(Math.min(waitTime, DEAD_LOCK_CHECK_INTERVAL)); + } catch (InterruptedException e) { + if (interruptable) { + throw e; + } + } + + if (done) { + return true; + } else { + waitTime = timeoutMillis + - (System.currentTimeMillis() - startTime); + if (waitTime <= 0) { + return done; + } + } + } + } finally { + waiters--; + if (!done) { + checkDeadLock(); + } + } + } + } + + private void checkDeadLock() { +// IllegalStateException e = new IllegalStateException( +// "DEAD LOCK: " + ChannelFuture.class.getSimpleName() + +// ".await() was invoked from an I/O processor thread. " + +// "Please use " + ChannelFutureListener.class.getSimpleName() + +// " or configure a proper thread model alternatively."); +// +// StackTraceElement[] stackTrace = e.getStackTrace(); +// +// // Simple and quick check. +// for (StackTraceElement s: stackTrace) { +// if (AbstractPollingIoProcessor.class.getName().equals(s.getClassName())) { +// throw e; +// } +// } +// +// // And then more precisely. +// for (StackTraceElement s: stackTrace) { +// try { +// Class cls = DefaultChannelFuture.class.getClassLoader().loadClass(s.getClassName()); +// if (IoProcessor.class.isAssignableFrom(cls)) { +// throw e; +// } +// } catch (Exception cnfe) { +// // Ignore +// } +// } + } + + public void setSuccess() { + synchronized (this) { + // Allow only once. + if (done) { + return; + } + + done = true; + if (waiters > 0) { + this.notifyAll(); + } + } + + notifyListeners(); + } + + public void setFailure(Throwable cause) { + synchronized (this) { + // Allow only once. + if (done) { + return; + } + + this.cause = cause; + done = true; + if (waiters > 0) { + this.notifyAll(); + } + } + + notifyListeners(); + } + + public boolean cancel() { + if (!cancellable) { + return false; + } + + synchronized (this) { + // Allow only once. + if (done) { + return false; + } + + cause = CANCELLED; + done = true; + if (waiters > 0) { + this.notifyAll(); + } + } + + notifyListeners(); + return true; + } + + private void notifyListeners() { + // There won't be any visibility problem or concurrent modification + // because 'ready' flag will be checked against both addListener and + // removeListener calls. + if (firstListener != null) { + notifyListener(firstListener); + firstListener = null; + + if (otherListeners != null) { + for (ChannelFutureListener l: otherListeners) { + notifyListener(l); + } + otherListeners = null; + } + + } + } + + private void notifyListener(ChannelFutureListener l) { + try { + l.operationComplete(this); + } catch (Throwable t) { + logger.warn( + "An exception was thrown by " + + ChannelFutureListener.class.getSimpleName() + ".", t); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultChannelPipeline.java b/src/main/java/net/gleamynode/netty/channel/DefaultChannelPipeline.java new file mode 100644 index 000000000000..b14c532dc4eb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultChannelPipeline.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import static net.gleamynode.netty.channel.ChannelPipelineCoverage.*; + +import java.lang.annotation.AnnotationFormatError; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +import net.gleamynode.netty.logging.Logger; + + +public class DefaultChannelPipeline implements ChannelPipeline { + + static final Logger logger = Logger.getLogger(DefaultChannelPipeline.class); + + private final ChannelSink discardingSink = new ChannelSink() { + public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) { + logger.warn("Not attached yet; discarding: " + e); + } + + public void exceptionCaught(ChannelPipeline pipeline, + ChannelEvent e, ChannelPipelineException cause) throws Exception { + throw cause; + } + }; + + private volatile Channel channel; + private volatile ChannelSink sink; + private volatile DefaultChannelHandlerContext head; + private volatile DefaultChannelHandlerContext tail; + private final Map name2ctx = + new HashMap(4); + + public Channel getChannel() { + return channel; + } + + public ChannelSink getSink() { + ChannelSink sink = this.sink; + if (sink == null) { + return discardingSink; + } + return sink; + } + + public void attach(Channel channel, ChannelSink sink) { + if (channel == null) { + throw new NullPointerException("channel"); + } + if (sink == null) { + throw new NullPointerException("sink"); + } + if (this.channel != null || this.sink != null) { + throw new IllegalStateException("attached already"); + } + this.channel = channel; + this.sink = sink; + } + + public synchronized void addFirst(String name, ChannelHandler handler) { + if (name2ctx.isEmpty()) { + init(name, handler); + } else { + checkDuplicateName(name); + DefaultChannelHandlerContext oldHead = head; + DefaultChannelHandlerContext newHead = new DefaultChannelHandlerContext(null, oldHead, name, handler); + oldHead.prev = newHead; + head = newHead; + name2ctx.put(name, newHead); + } + } + + public synchronized void addLast(String name, ChannelHandler handler) { + if (name2ctx.isEmpty()) { + init(name, handler); + } else { + checkDuplicateName(name); + DefaultChannelHandlerContext oldTail = tail; + DefaultChannelHandlerContext newTail = new DefaultChannelHandlerContext(oldTail, null, name, handler); + oldTail.next = newTail; + tail = newTail; + name2ctx.put(name, newTail); + } + } + + public synchronized void addBefore(String baseName, String name, ChannelHandler handler) { + DefaultChannelHandlerContext ctx = getContextOrDie(baseName); + if (ctx == head) { + addFirst(name, handler); + } else { + checkDuplicateName(name); + DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(ctx.prev, ctx, name, handler); + ctx.prev.next = newCtx; + ctx.prev = newCtx; + name2ctx.put(name, newCtx); + } + } + + public synchronized void addAfter(String baseName, String name, ChannelHandler handler) { + DefaultChannelHandlerContext ctx = getContextOrDie(baseName); + if (ctx == tail) { + addLast(name, handler); + } else { + checkDuplicateName(name); + DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(ctx, ctx.next, name, handler); + ctx.next.prev = newCtx; + ctx.next = newCtx; + name2ctx.put(name, newCtx); + } + } + + public synchronized void remove(ChannelHandler handler) { + remove(getContextOrDie(handler)); + } + + public synchronized ChannelHandler remove(String name) { + return remove(getContextOrDie(name)).getHandler(); + } + + @SuppressWarnings("unchecked") + public synchronized T remove(Class handlerType) { + return (T) remove(getContextOrDie(handlerType)).getHandler(); + } + + private DefaultChannelHandlerContext remove(DefaultChannelHandlerContext ctx) { + if (head == tail) { + head = tail = null; + name2ctx.clear(); + } else if (ctx == head) { + removeFirst(); + } else if (ctx == tail) { + removeLast(); + } else { + DefaultChannelHandlerContext prev = ctx.prev; + DefaultChannelHandlerContext next = ctx.next; + prev.next = next; + next.prev = prev; + name2ctx.remove(ctx.getName()); + } + return ctx; + } + + public synchronized ChannelHandler removeFirst() { + if (name2ctx.isEmpty()) { + throw new NoSuchElementException(); + } + + DefaultChannelHandlerContext oldHead = head; + oldHead.next.prev = null; + head = oldHead.next; + name2ctx.remove(oldHead.getName()); + return oldHead.getHandler(); + } + + public synchronized ChannelHandler removeLast() { + if (name2ctx.isEmpty()) { + throw new NoSuchElementException(); + } + + DefaultChannelHandlerContext oldTail = tail; + oldTail.prev.next = null; + tail = oldTail.prev; + name2ctx.remove(oldTail.getName()); + return oldTail.getHandler(); + } + + public synchronized void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) { + replace(getContextOrDie(oldHandler), newName, newHandler); + } + + public synchronized ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler) { + return replace(getContextOrDie(oldName), newName, newHandler); + } + + @SuppressWarnings("unchecked") + public synchronized T replace( + Class oldHandlerType, String newName, ChannelHandler newHandler) { + return (T) replace(getContextOrDie(oldHandlerType), newName, newHandler); + } + + private ChannelHandler replace(DefaultChannelHandlerContext ctx, String newName, ChannelHandler newHandler) { + if (ctx == head) { + removeFirst(); + addFirst(newName, newHandler); + } else if (ctx == tail) { + removeLast(); + addLast(newName, newHandler); + } else { + boolean sameName = ctx.getName().equals(newName); + if (!sameName) { + checkDuplicateName(newName); + } + DefaultChannelHandlerContext prev = ctx.prev; + DefaultChannelHandlerContext next = ctx.next; + DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(prev, next, newName, newHandler); + prev.next = newCtx; + next.prev = newCtx; + if (!sameName) { + name2ctx.remove(ctx.getName()); + name2ctx.put(newName, newCtx); + } + } + return ctx.getHandler(); + } + + public synchronized ChannelHandler getFirst() { + return head.getHandler(); + } + + public synchronized ChannelHandler getLast() { + return tail.getHandler(); + } + + public synchronized ChannelHandler get(String name) { + DefaultChannelHandlerContext ctx = name2ctx.get(name); + if (ctx == null) { + return null; + } else { + return ctx.getHandler(); + } + } + + @SuppressWarnings("unchecked") + public synchronized T get(Class handlerType) { + ChannelHandlerContext ctx = getContext(handlerType); + if (ctx == null) { + return null; + } else { + return (T) ctx.getHandler(); + } + } + + public synchronized ChannelHandlerContext getContext(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + return name2ctx.get(name); + } + + public synchronized ChannelHandlerContext getContext(ChannelHandler handler) { + if (handler == null) { + throw new NullPointerException("handler"); + } + if (name2ctx.isEmpty()) { + return null; + } + DefaultChannelHandlerContext ctx = head; + for (;;) { + if (ctx.getHandler() == handler) { + return ctx; + } + + ctx = ctx.next; + if (ctx == null) { + break; + } + } + return null; + } + + public synchronized ChannelHandlerContext getContext( + Class handlerType) { + if (name2ctx.isEmpty()) { + return null; + } + DefaultChannelHandlerContext ctx = head; + for (;;) { + if (handlerType.isAssignableFrom(ctx.getHandler().getClass())) { + return ctx; + } + + ctx = ctx.next; + if (ctx == null) { + break; + } + } + return null; + } + + public Map toMap() { + Map map = new LinkedHashMap(); + if (name2ctx.isEmpty()) { + return map; + } + + DefaultChannelHandlerContext ctx = head; + for (;;) { + map.put(ctx.getName(), ctx.getHandler()); + ctx = ctx.next; + if (ctx == null) { + break; + } + } + return map; + } + + public void sendUpstream(ChannelEvent e) { + DefaultChannelHandlerContext head = getActualUpstreamContext(this.head); + if (head == null) { + logger.warn( + "The pipeline contains no upstream handlers; discarding: " + e); + return; + } + + sendUpstream(head, e); + } + + void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) { + try { + ((ChannelUpstreamHandler) ctx.getHandler()).handleUpstream(ctx, e); + } catch (Throwable t) { + notifyException(e, t); + } + } + + public void sendDownstream(ChannelEvent e) { + DefaultChannelHandlerContext tail = getActualDownstreamContext(this.tail); + if (tail == null) { + try { + getSink().eventSunk(this, e); + return; + } catch (Throwable t) { + notifyException(e, t); + } + } + + sendDownstream(tail, e); + } + + void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) { + try { + ((ChannelDownstreamHandler) ctx.getHandler()).handleDownstream(ctx, e); + } catch (Throwable t) { + notifyException(e, t); + } + } + + DefaultChannelHandlerContext getActualUpstreamContext(DefaultChannelHandlerContext ctx) { + if (ctx == null) { + return null; + } + + DefaultChannelHandlerContext realCtx = ctx; + while (!realCtx.canHandleUpstream()) { + realCtx = realCtx.next; + if (realCtx == null) { + return null; + } + } + + return realCtx; + } + + DefaultChannelHandlerContext getActualDownstreamContext(DefaultChannelHandlerContext ctx) { + if (ctx == null) { + return null; + } + + DefaultChannelHandlerContext realCtx = ctx; + while (!realCtx.canHandleDownstream()) { + realCtx = realCtx.prev; + if (realCtx == null) { + return null; + } + } + + return realCtx; + } + + void notifyException(ChannelEvent e, Throwable t) { + ChannelPipelineException pe; + if (t instanceof ChannelPipelineException) { + pe = (ChannelPipelineException) t; + } else { + pe = new ChannelPipelineException(t); + } + + try { + sink.exceptionCaught(this, e, pe); + } catch (Exception e1) { + logger.warn("An exception was thrown by an exception handler.", e1); + } + } + + private void init(String name, ChannelHandler handler) { + DefaultChannelHandlerContext ctx = new DefaultChannelHandlerContext(null, null, name, handler); + head = tail = ctx; + name2ctx.clear(); + name2ctx.put(name, ctx); + } + + private void checkDuplicateName(String name) { + if (name2ctx.containsKey(name)) { + throw new IllegalArgumentException("Duplicate handler name."); + } + } + + private DefaultChannelHandlerContext getContextOrDie(String name) { + DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(name); + if (ctx == null) { + throw new NoSuchElementException(name); + } else { + return ctx; + } + } + + private DefaultChannelHandlerContext getContextOrDie(ChannelHandler handler) { + DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(handler); + if (ctx == null) { + throw new NoSuchElementException(handler.getClass().getName()); + } else { + return ctx; + } + } + + private DefaultChannelHandlerContext getContextOrDie(Class handlerType) { + DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(handlerType); + if (ctx == null) { + throw new NoSuchElementException(handlerType.getName()); + } else { + return ctx; + } + } + + private class DefaultChannelHandlerContext implements ChannelHandlerContext { + volatile DefaultChannelHandlerContext next; + volatile DefaultChannelHandlerContext prev; + private final String name; + private final ChannelHandler handler; + private final boolean canHandleUpstream; + private final boolean canHandleDownstream; + + DefaultChannelHandlerContext( + DefaultChannelHandlerContext prev, DefaultChannelHandlerContext next, + String name, ChannelHandler handler) { + + if (name == null) { + throw new NullPointerException("name"); + } + if (handler == null) { + throw new NullPointerException("handler"); + } + canHandleUpstream = handler instanceof ChannelUpstreamHandler; + canHandleDownstream = handler instanceof ChannelDownstreamHandler; + + + if (!canHandleUpstream && !canHandleDownstream) { + throw new IllegalArgumentException( + "handler must be either " + + ChannelUpstreamHandler.class.getName() + " or " + + ChannelDownstreamHandler.class.getName() + '.'); + } + + + ChannelPipelineCoverage coverage = handler.getClass().getAnnotation(ChannelPipelineCoverage.class); + if (coverage == null) { + logger.warn( + "Handler '" + handler.getClass().getName() + + "' doesn't have a '" + + ChannelPipelineCoverage.class.getSimpleName() + + "' annotation with its class declaration. " + + "It is recommended to add the annotation to tell if " + + "one handler instance can handle more than one pipeline " + + "(\"" + ALL + "\") or not (\"" + ONE + "\")"); + } else { + String coverageValue = coverage.value(); + if (coverageValue == null) { + throw new AnnotationFormatError( + ChannelPipelineCoverage.class.getSimpleName() + + " annotation value is undefined for type: " + + handler.getClass().getName()); + } + + + if (!coverageValue.equals(ALL) && !coverageValue.equals(ONE)) { + throw new AnnotationFormatError( + ChannelPipelineCoverage.class.getSimpleName() + + " annotation value: " + coverageValue + + " (must be either \"" + ALL + "\" or \"" + ONE + ")"); + } + } + + this.prev = prev; + this.next = next; + this.name = name; + this.handler = handler; + } + + public ChannelPipeline getPipeline() { + return DefaultChannelPipeline.this; + } + + public boolean canHandleDownstream() { + return canHandleDownstream; + } + + public boolean canHandleUpstream() { + return canHandleUpstream; + } + + public ChannelHandler getHandler() { + return handler; + } + + public String getName() { + return name; + } + + public void sendDownstream(ChannelEvent e) { + DefaultChannelHandlerContext prev = getActualDownstreamContext(this.prev); + if (prev == null) { + try { + getSink().eventSunk(DefaultChannelPipeline.this, e); + } catch (Throwable t) { + notifyException(e, t); + } + } else { + DefaultChannelPipeline.this.sendDownstream(prev, e); + } + } + + public void sendUpstream(ChannelEvent e) { + DefaultChannelHandlerContext next = getActualUpstreamContext(this.next); + if (next != null) { + DefaultChannelPipeline.this.sendUpstream(next, e); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultChannelStateEvent.java b/src/main/java/net/gleamynode/netty/channel/DefaultChannelStateEvent.java new file mode 100644 index 000000000000..44b5692d1187 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultChannelStateEvent.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +public class DefaultChannelStateEvent extends DefaultChannelEvent implements + ChannelStateEvent { + + private final ChannelState state; + private final Object value; + + public DefaultChannelStateEvent( + Channel channel, ChannelFuture future, + ChannelState state, Object value) { + + super(channel, future); + if (state == null) { + throw new NullPointerException("state"); + } + this.state = state; + this.value = value; + } + + public ChannelState getState() { + return state; + } + + public Object getValue() { + return value; + } + + @Override + public String toString() { + String parentString = super.toString(); + StringBuilder buf = new StringBuilder(parentString.length() + 64); + buf.append(parentString); + buf.append(" - (state: "); + switch (getState()) { + case OPEN: + if (Boolean.TRUE.equals(getValue())) { + buf.append("OPEN"); + } else { + buf.append("CLOSED"); + } + break; + case BOUND: + if (getValue() != null) { + buf.append("BOUND"); + } else { + buf.append("UNBOUND"); + } + break; + case CONNECTED: + if (getValue() != null) { + buf.append("CONNECTED"); + } else { + buf.append("DISCONNECTED"); + } + break; + case INTEREST_OPS: + switch (((Integer) getValue()).intValue()) { + case Channel.OP_NONE: + buf.append("OP_NONE"); + break; + case Channel.OP_READ: + buf.append("OP_READ"); + break; + case Channel.OP_WRITE: + buf.append("OP_WRITE"); + break; + case Channel.OP_READ_WRITE: + buf.append("OP_READ_WRITE"); + break; + default: + buf.append("OP_"); + buf.append(getValue()); + buf.append(" (?)"); + } + } + buf.append(')'); + return buf.toString(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultChildChannelStateEvent.java b/src/main/java/net/gleamynode/netty/channel/DefaultChildChannelStateEvent.java new file mode 100644 index 000000000000..4e5bbe4538a2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultChildChannelStateEvent.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +public class DefaultChildChannelStateEvent extends DefaultChannelEvent implements + ChildChannelStateEvent { + + private final Channel childChannel; + + public DefaultChildChannelStateEvent( + Channel channel, ChannelFuture future, Channel childChannel) { + + super(channel, future); + if (childChannel == null) { + throw new NullPointerException("childChannel"); + } + this.childChannel = childChannel; + } + + public Channel getChildChannel() { + return childChannel; + } + + @Override + public String toString() { + String parentString = super.toString(); + StringBuilder buf = new StringBuilder(parentString.length() + 32); + buf.append(parentString); + buf.append(" - (childId: "); + buf.append(getChildChannel().getId().toString()); + buf.append(", childState: "); + buf.append(getChildChannel().isOpen()? "OPEN" : "CLOSE"); + buf.append(')'); + return buf.toString(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultExceptionEvent.java b/src/main/java/net/gleamynode/netty/channel/DefaultExceptionEvent.java new file mode 100644 index 000000000000..15f129988c20 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultExceptionEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +public class DefaultExceptionEvent extends DefaultChannelEvent implements + ExceptionEvent { + + private final Throwable cause; + + public DefaultExceptionEvent(Channel channel, ChannelFuture future, Throwable cause) { + super(channel, future); + if (cause == null) { + throw new NullPointerException("cause"); + } + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } + + @Override + public String toString() { + return super.toString() + " - (cause: " + cause.getClass().getSimpleName() + ')'; + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/DefaultMessageEvent.java b/src/main/java/net/gleamynode/netty/channel/DefaultMessageEvent.java new file mode 100644 index 000000000000..1d5892d034a4 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/DefaultMessageEvent.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; + +public class DefaultMessageEvent extends DefaultChannelEvent implements + MessageEvent { + + private final Object message; + private final SocketAddress remoteAddress; + + public DefaultMessageEvent( + Channel channel, ChannelFuture future, + Object message, SocketAddress remoteAddress) { + + super(channel, future); + if (message == null) { + throw new NullPointerException("message"); + } + this.message = message; + this.remoteAddress = remoteAddress; + } + + public Object getMessage() { + return message; + } + + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + + @Override + public String toString() { + if (remoteAddress == null) { + return super.toString() + " - (message: " + message + ')'; + } else { + return super.toString() + " - (message: " + message + ", " + remoteAddress + ')'; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/ExceptionEvent.java b/src/main/java/net/gleamynode/netty/channel/ExceptionEvent.java new file mode 100644 index 000000000000..c1a97ad1f2eb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/ExceptionEvent.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public interface ExceptionEvent extends ChannelEvent { + Throwable getCause(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/FailedChannelFuture.java b/src/main/java/net/gleamynode/netty/channel/FailedChannelFuture.java new file mode 100644 index 000000000000..b69671e34261 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/FailedChannelFuture.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +public class FailedChannelFuture extends CompleteChannelFuture { + + private final Throwable cause; + + public FailedChannelFuture(Channel channel, Throwable cause) { + super(channel); + if (cause == null) { + throw new NullPointerException("cause"); + } + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } + + public boolean isSuccess() { + return false; + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/MessageEvent.java b/src/main/java/net/gleamynode/netty/channel/MessageEvent.java new file mode 100644 index 000000000000..281006620ebc --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/MessageEvent.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import java.net.SocketAddress; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public interface MessageEvent extends ChannelEvent { + Object getMessage(); + SocketAddress getRemoteAddress(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/SimpleChannelHandler.java b/src/main/java/net/gleamynode/netty/channel/SimpleChannelHandler.java new file mode 100644 index 000000000000..b8a6c249e332 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/SimpleChannelHandler.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + +import net.gleamynode.netty.logging.Logger; + + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public class SimpleChannelHandler implements ChannelUpstreamHandler { + + private static final Logger logger = + Logger.getLogger(SimpleChannelHandler.class.getName()); + + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + + if (e instanceof MessageEvent) { + messageReceived(ctx, (MessageEvent) e); + } else if (e instanceof ChildChannelStateEvent) { + ChildChannelStateEvent evt = (ChildChannelStateEvent) e; + if (evt.getChildChannel().isOpen()) { + childChannelOpen(ctx, evt); + } else { + childChannelClosed(ctx, evt); + } + } else if (e instanceof ChannelStateEvent) { + ChannelStateEvent evt = (ChannelStateEvent) e; + switch (evt.getState()) { + case OPEN: + if (Boolean.TRUE.equals(evt.getValue())) { + channelOpen(ctx, evt); + } else { + channelClosed(ctx, evt); + } + break; + case BOUND: + if (evt.getValue() != null) { + channelBound(ctx, evt); + } else { + channelUnbound(ctx, evt); + } + break; + case CONNECTED: + if (evt.getValue() != null) { + channelConnected(ctx, evt); + } else { + channelDisconnected(ctx, evt); + } + break; + case INTEREST_OPS: + channelInterestChanged(ctx, evt); + break; + } + } else if (e instanceof ExceptionEvent) { + exceptionCaught(ctx, (ExceptionEvent) e); + } else { + ctx.sendUpstream(e); + } + } + + public void channelBound( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelClosed( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelInterestChanged( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelDisconnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelOpen( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void channelUnbound( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + if (this == ctx.getPipeline().getLast()) { + logger.warn( + "EXCEPTION, please implement " + getClass().getName() + + ".exceptionCaught() for proper handling.", e.getCause()); + } + ctx.sendUpstream(e); + } + + public void childChannelClosed( + ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } + + public void childChannelOpen( + ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception { + ctx.sendUpstream(e); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/SucceededChannelFuture.java b/src/main/java/net/gleamynode/netty/channel/SucceededChannelFuture.java new file mode 100644 index 000000000000..014fb3272a15 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/SucceededChannelFuture.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel; + + +public class SucceededChannelFuture extends CompleteChannelFuture { + + public SucceededChannelFuture(Channel channel) { + super(channel); + } + + public Throwable getCause() { + return null; + } + + public boolean isSuccess() { + return true; + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/package-info.java b/src/main/java/net/gleamynode/netty/channel/package-info.java new file mode 100644 index 000000000000..327af5065778 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/package-info.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * The core channel API which is asynchronous and event-driven abstraction of + * various transports such as a + * NIO Channel. + * + * @apiviz.landmark + * @apiviz.exclude ^java + * @apiviz.exclude ^net\.gleamynode\.netty\.channel\.[^\.]+\. + * @apiviz.exclude ^net\.gleamynode\.netty\.(bootstrap|handler)\. + * @apiviz.exclude \.(Abstract|Default).*$ + * @apiviz.exclude \.[A-Za-z]+ChannelFuture$ + * @apiviz.exclude \.ChannelState$ + */ +package net.gleamynode.netty.channel; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/socket/ClientSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/ClientSocketChannelFactory.java new file mode 100644 index 000000000000..e9e315f2fd97 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/ClientSocketChannelFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelPipeline; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.socket.SocketChannel oneway - - creates + */ +public interface ClientSocketChannelFactory extends ChannelFactory { + SocketChannel newChannel(ChannelPipeline pipeline); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/DefaultServerSocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/DefaultServerSocketChannelConfig.java new file mode 100644 index 000000000000..c587020bcbf5 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/DefaultServerSocketChannelConfig.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import java.net.ServerSocket; +import java.net.SocketException; +import java.util.Map; +import java.util.Map.Entry; + +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelPipelineFactory; +import net.gleamynode.netty.util.ConvertUtil; + +public class DefaultServerSocketChannelConfig implements ServerSocketChannelConfig { + + private final ServerSocket socket; + private volatile int backlog; + private volatile ChannelPipelineFactory pipelineFactory; + + public DefaultServerSocketChannelConfig(ServerSocket socket) { + if (socket == null) { + throw new NullPointerException("socket"); + } + this.socket = socket; + } + + public void setOptions(Map options) { + for (Entry e: options.entrySet()) { + setOption(e.getKey(), e.getValue()); + } + } + + protected boolean setOption(String key, Object value) { + if (key.equals("receiveBufferSize")) { + setReceiveBufferSize(ConvertUtil.toInt(value)); + } else if (key.equals("reuseAddress")) { + setReuseAddress(ConvertUtil.toBoolean(value)); + } else if (key.equals("backlog")) { + setBacklog(ConvertUtil.toInt(value)); + } else { + return false; + } + return true; + } + + public boolean isReuseAddress() { + try { + return socket.getReuseAddress(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setReuseAddress(boolean reuseAddress) { + try { + socket.setReuseAddress(reuseAddress); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public int getReceiveBufferSize() { + try { + return socket.getReceiveBufferSize(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setReceiveBufferSize(int receiveBufferSize) { + try { + socket.setReceiveBufferSize(receiveBufferSize); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + socket.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + public ChannelPipelineFactory getPipelineFactory() { + return pipelineFactory; + } + + public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) { + if (pipelineFactory == null) { + throw new NullPointerException("pipelineFactory"); + } + this.pipelineFactory = pipelineFactory; + } + + public int getBacklog() { + return backlog; + } + + public void setBacklog(int backlog) { + if (backlog < 1) { + throw new IllegalArgumentException("backlog: " + backlog); + } + this.backlog = backlog; + } + + public int getConnectTimeoutMillis() { + return 0; + } + + public void setConnectTimeoutMillis(int connectTimeoutMillis) { + // Unused + } + + public int getWriteTimeoutMillis() { + return 0; + } + + public void setWriteTimeoutMillis(int writeTimeoutMillis) { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/DefaultSocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/DefaultSocketChannelConfig.java new file mode 100644 index 000000000000..1a3f6afee28b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/DefaultSocketChannelConfig.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import java.net.Socket; +import java.net.SocketException; +import java.util.Map; +import java.util.Map.Entry; + +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelPipelineFactory; +import net.gleamynode.netty.util.ConvertUtil; + +public class DefaultSocketChannelConfig implements SocketChannelConfig { + + private final Socket socket; + private volatile int connectTimeoutMillis = 10000; // 10 seconds + + public DefaultSocketChannelConfig(Socket socket) { + if (socket == null) { + throw new NullPointerException("socket"); + } + this.socket = socket; + } + + public void setOptions(Map options) { + for (Entry e: options.entrySet()) { + setOption(e.getKey(), e.getValue()); + } + } + + protected boolean setOption(String key, Object value) { + if (key.equals("receiveBufferSize")) { + setReceiveBufferSize(ConvertUtil.toInt(value)); + } else if (key.equals("sendBufferSize")) { + setSendBufferSize(ConvertUtil.toInt(value)); + } else if (key.equals("tcpNoDelay")) { + setTcpNoDelay(ConvertUtil.toBoolean(value)); + } else if (key.equals("keepAlive")) { + setKeepAlive(ConvertUtil.toBoolean(value)); + } else if (key.equals("reuseAddress")) { + setReuseAddress(ConvertUtil.toBoolean(value)); + } else if (key.equals("soLinger")) { + setSoLinger(ConvertUtil.toInt(value)); + } else if (key.equals("trafficClass")) { + setTrafficClass(ConvertUtil.toInt(value)); + } else if (key.equals("writeTimeoutMillis")) { + setWriteTimeoutMillis(ConvertUtil.toInt(value)); + } else if (key.equals("connectTimeoutMillis")) { + setConnectTimeoutMillis(ConvertUtil.toInt(value)); + } else if (key.equals("pipelineFactory")) { + setPipelineFactory((ChannelPipelineFactory) value); + } else { + return false; + } + return true; + } + + public int getReceiveBufferSize() { + try { + return socket.getReceiveBufferSize(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public int getSendBufferSize() { + try { + return socket.getSendBufferSize(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public int getSoLinger() { + try { + return socket.getSoLinger(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public int getTrafficClass() { + try { + return socket.getTrafficClass(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public boolean isKeepAlive() { + try { + return socket.getKeepAlive(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public boolean isReuseAddress() { + try { + return socket.getReuseAddress(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public boolean isTcpNoDelay() { + try { + return socket.getTcpNoDelay(); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setKeepAlive(boolean keepAlive) { + try { + socket.setKeepAlive(keepAlive); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setPerformancePreferences( + int connectionTime, int latency, int bandwidth) { + socket.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + public void setReceiveBufferSize(int receiveBufferSize) { + try { + socket.setReceiveBufferSize(receiveBufferSize); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setReuseAddress(boolean reuseAddress) { + try { + socket.setReuseAddress(reuseAddress); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setSendBufferSize(int sendBufferSize) { + try { + socket.setSendBufferSize(sendBufferSize); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setSoLinger(int soLinger) { + try { + if (soLinger < 0) { + socket.setSoLinger(false, 0); + } else { + socket.setSoLinger(true, soLinger); + } + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setTcpNoDelay(boolean tcpNoDelay) { + try { + socket.setTcpNoDelay(tcpNoDelay); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public void setTrafficClass(int trafficClass) { + try { + socket.setTrafficClass(trafficClass); + } catch (SocketException e) { + throw new ChannelException(e); + } + } + + public int getConnectTimeoutMillis() { + return connectTimeoutMillis; + } + + public ChannelPipelineFactory getPipelineFactory() { + return null; + } + + public int getWriteTimeoutMillis() { + return 0; + } + + public void setConnectTimeoutMillis(int connectTimeoutMillis) { + if (connectTimeoutMillis < 0) { + throw new IllegalArgumentException("connectTimeoutMillis: " + connectTimeoutMillis); + } + this.connectTimeoutMillis = connectTimeoutMillis; + } + + public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) { + // Unused + } + + public void setWriteTimeoutMillis(int writeTimeoutMillis) { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannel.java new file mode 100644 index 000000000000..af7b50886a8e --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannel.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import java.net.InetSocketAddress; + +import net.gleamynode.netty.channel.Channel; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.composedOf net.gleamynode.netty.channel.socket.ServerSocketChannelConfig + */ +public interface ServerSocketChannel extends Channel { + ServerSocketChannelConfig getConfig(); + InetSocketAddress getLocalAddress(); + InetSocketAddress getRemoteAddress(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelConfig.java new file mode 100644 index 000000000000..fd43fc116085 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import net.gleamynode.netty.channel.ChannelConfig; + +public interface ServerSocketChannelConfig extends ChannelConfig { + int getBacklog(); + void setBacklog(int backlog); + boolean isReuseAddress(); + void setReuseAddress(boolean reuseAddress); + int getReceiveBufferSize(); + void setReceiveBufferSize(int receiveBufferSize); + void setPerformancePreferences(int connectionTime, int latency, int bandwidth); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelFactory.java new file mode 100644 index 000000000000..373373b0c159 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/ServerSocketChannelFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelPipeline; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.socket.ServerSocketChannel oneway - - creates + */ +public interface ServerSocketChannelFactory extends ChannelFactory { + ServerSocketChannel newChannel(ChannelPipeline pipeline); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/SocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/SocketChannel.java new file mode 100644 index 000000000000..5df2a79786ef --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/SocketChannel.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import java.net.InetSocketAddress; + +import net.gleamynode.netty.channel.Channel; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.composedOf net.gleamynode.netty.channel.socket.SocketChannelConfig + */ +public interface SocketChannel extends Channel { + SocketChannelConfig getConfig(); + InetSocketAddress getLocalAddress(); + InetSocketAddress getRemoteAddress(); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/SocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/SocketChannelConfig.java new file mode 100644 index 000000000000..7537e7a04681 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/SocketChannelConfig.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket; + +import net.gleamynode.netty.channel.ChannelConfig; + +public interface SocketChannelConfig extends ChannelConfig { + boolean isTcpNoDelay(); + void setTcpNoDelay(boolean tcpNoDelay); + int getSoLinger(); + void setSoLinger(int soLinger); + int getSendBufferSize(); + void setSendBufferSize(int sendBufferSize); + int getReceiveBufferSize(); + void setReceiveBufferSize(int receiveBufferSize); + boolean isKeepAlive(); + void setKeepAlive(boolean keepAlive); + int getTrafficClass(); + void setTrafficClass(int trafficClass); + boolean isReuseAddress(); + void setReuseAddress(boolean reuseAddress); + void setPerformancePreferences( + int connectionTime, int latency, int bandwidth); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java new file mode 100644 index 000000000000..ef3638da9c32 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultNioSocketChannelConfig.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.net.Socket; + +import net.gleamynode.netty.channel.socket.DefaultSocketChannelConfig; +import net.gleamynode.netty.util.ConvertUtil; + +class DefaultNioSocketChannelConfig extends DefaultSocketChannelConfig + implements NioSocketChannelConfig { + + private volatile ReceiveBufferSizePredictor predictor = + new DefaultReceiveBufferSizePredictor(); + private volatile int writeSpinCount = 16; + private volatile boolean readWriteFair; + + DefaultNioSocketChannelConfig(Socket socket) { + super(socket); + } + + @Override + protected boolean setOption(String key, Object value) { + if (super.setOption(key, value)) { + return true; + } + + if (key.equals("readWriteFair")) { + setReadWriteFair(ConvertUtil.toBoolean(value)); + } else if (key.equals("writeSpinCount")) { + setWriteSpinCount(ConvertUtil.toInt(value)); + } else if (key.equals("receiveBufferSizePredictor")) { + setReceiveBufferSizePredictor((ReceiveBufferSizePredictor) value); + } else { + return false; + } + return true; + } + + public int getWriteSpinCount() { + return writeSpinCount; + } + + public void setWriteSpinCount(int writeSpinCount) { + if (writeSpinCount <= 0) { + throw new IllegalArgumentException( + "writeSpinCount must be a positive integer."); + } + } + + public ReceiveBufferSizePredictor getReceiveBufferSizePredictor() { + return predictor; + } + + public void setReceiveBufferSizePredictor( + ReceiveBufferSizePredictor predictor) { + if (predictor == null) { + throw new NullPointerException("predictor"); + } + this.predictor = predictor; + } + + public boolean isReadWriteFair() { + return readWriteFair; + } + + public void setReadWriteFair(boolean readWriteFair) { + this.readWriteFair = readWriteFair; + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultReceiveBufferSizePredictor.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultReceiveBufferSizePredictor.java new file mode 100644 index 000000000000..44a5e50a5dd1 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/DefaultReceiveBufferSizePredictor.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +public class DefaultReceiveBufferSizePredictor implements + ReceiveBufferSizePredictor { + private static final int DEFAULT_MINIMUM = 256; + private static final int DEFAULT_INITIAL = 1024; + private static final int DEFAULT_MAXIMUM = 1048576; + + private final int minimum; + private final int maximum; + private int nextReceiveBufferSize = 1024; + private boolean shouldHalveNow; + + public DefaultReceiveBufferSizePredictor() { + this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM); + } + + public DefaultReceiveBufferSizePredictor(int minimum, int initial, int maximum) { + if (minimum <= 0) { + throw new IllegalArgumentException("minimum: " + minimum); + } + if (initial < minimum) { + throw new IllegalArgumentException("initial: " + initial); + } + if (maximum < initial) { + throw new IllegalArgumentException("maximum: " + maximum); + } + this.minimum = minimum; + nextReceiveBufferSize = initial; + this.maximum = maximum; + } + + public int nextReceiveBufferSize() { + return nextReceiveBufferSize; + } + + public void previousReceiveBufferSize(int previousReceiveBufferSize) { + if (previousReceiveBufferSize < nextReceiveBufferSize >>> 1) { + if (shouldHalveNow) { + nextReceiveBufferSize = Math.max(minimum, nextReceiveBufferSize >>> 1); + shouldHalveNow = false; + } else { + shouldHalveNow = true; + } + } else if (previousReceiveBufferSize == nextReceiveBufferSize) { + nextReceiveBufferSize = Math.min(maximum, nextReceiveBufferSize << 1); + shouldHalveNow = false; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioAcceptedSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioAcceptedSocketChannel.java new file mode 100644 index 000000000000..228f6a2c4f2f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioAcceptedSocketChannel.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.io.IOException; +import java.nio.channels.SocketChannel; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; + +class NioAcceptedSocketChannel extends NioSocketChannel { + + final NioWorker worker; + + NioAcceptedSocketChannel( + ChannelFactory factory, ChannelPipeline pipeline, + Channel parent, ChannelSink sink, + SocketChannel socket, NioWorker worker) { + + super(parent, factory, pipeline, sink, socket); + + this.worker = worker; + try { + socket.configureBlocking(false); + } catch (IOException e) { + throw new ChannelException("Failed to enter non-blocking mode.", e); + } + } + + @Override + NioWorker getWorker() { + return worker; + } + + @Override + void setWorker(NioWorker worker) { + // worker never changes. + if (this.worker != worker) { + throw new IllegalStateException("Should not reach here."); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannel.java new file mode 100644 index 000000000000..48a31fb83f1a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannel.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.nio.channels.SocketChannel; + +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.logging.Logger; + +class NioClientSocketChannel extends NioSocketChannel { + + private static final Logger logger = + Logger.getLogger(NioClientSocketChannel.class); + + private static SocketChannel newSocket() { + SocketChannel socket; + try { + socket = SocketChannel.open(); + } catch (IOException e) { + throw new ChannelException("Failed to open a socket.", e); + } + + boolean success = false; + try { + socket.configureBlocking(false); + success = true; + } catch (IOException e) { + throw new ChannelException("Failed to enter non-blocking mode.", e); + } finally { + if (!success) { + try { + socket.close(); + } catch (IOException e) { + logger.warn( + "Failed to close a partially initialized socket.", + e); + } + } + } + + return socket; + } + + volatile NioWorker worker; + volatile ChannelFuture connectFuture; + volatile boolean boundManually; + + NioClientSocketChannel( + ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink) { + + super(null, factory, pipeline, sink, newSocket()); + fireChannelOpen(this); + } + + @Override + NioWorker getWorker() { + return worker; + } + + @Override + void setWorker(NioWorker worker) { + if (this.worker == null) { + this.worker = worker; + } else if (this.worker != worker) { + // worker never changes. + throw new IllegalStateException("Should not reach here."); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannelFactory.java new file mode 100644 index 000000000000..4af619d237e2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketChannelFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.ClientSocketChannelFactory; +import net.gleamynode.netty.channel.socket.SocketChannel; + +public class NioClientSocketChannelFactory implements ClientSocketChannelFactory { + + private final ChannelSink sink; + + public NioClientSocketChannelFactory( + Executor bossExecutor, Executor workerExecutor) { + this(bossExecutor, workerExecutor, Runtime.getRuntime().availableProcessors()); + } + + public NioClientSocketChannelFactory( + Executor bossExecutor, Executor workerExecutor, + int workerCount) { + if (bossExecutor == null) { + throw new NullPointerException("bossExecutor"); + } + if (workerExecutor == null) { + throw new NullPointerException("workerExecutor"); + } + if (workerCount <= 0) { + throw new IllegalArgumentException( + "workerCount (" + workerCount + ") " + + "must be a positive integer."); + } + sink = new NioClientSocketPipelineSink(bossExecutor, workerExecutor, workerCount); + } + + public SocketChannel newChannel(ChannelPipeline pipeline) { + return new NioClientSocketChannel(this, pipeline, sink); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketPipelineSink.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketPipelineSink.java new file mode 100644 index 000000000000..7023a8579a2a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioClientSocketPipelineSink.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import net.gleamynode.netty.channel.AbstractChannelSink; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.logging.Logger; +import net.gleamynode.netty.util.NamePreservingRunnable; + +class NioClientSocketPipelineSink extends AbstractChannelSink { + + static final Logger logger = + Logger.getLogger(NioClientSocketPipelineSink.class); + private static final AtomicInteger nextId = new AtomicInteger(); + + final int id = nextId.incrementAndGet(); + final Executor bossExecutor; + private final Boss boss = new Boss(); + private final NioWorker[] workers; + private final AtomicInteger workerIndex = new AtomicInteger(); + + NioClientSocketPipelineSink( + Executor bossExecutor, Executor workerExecutor, int workerCount) { + this.bossExecutor = bossExecutor; + workers = new NioWorker[workerCount]; + for (int i = 0; i < workers.length; i ++) { + workers[i] = new NioWorker(id, i + 1, workerExecutor); + } + } + + public void eventSunk( + ChannelPipeline pipeline, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + ChannelStateEvent event = (ChannelStateEvent) e; + NioClientSocketChannel channel = + (NioClientSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + ChannelState state = event.getState(); + Object value = event.getValue(); + + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + NioWorker.close(channel, future); + } + break; + case BOUND: + if (value != null) { + bind(channel, future, (SocketAddress) value); + } else { + NioWorker.close(channel, future); + } + break; + case CONNECTED: + if (value != null) { + connect(channel, future, (SocketAddress) value); + } else { + NioWorker.close(channel, future); + } + break; + case INTEREST_OPS: + NioWorker.setInterestOps(channel, future, ((Integer) value).intValue()); + break; + } + } else if (e instanceof MessageEvent) { + MessageEvent event = (MessageEvent) e; + NioSocketChannel channel = (NioSocketChannel) event.getChannel(); + channel.writeBuffer.offer(event); + NioWorker.write(channel); + } + } + + private void bind( + NioClientSocketChannel channel, ChannelFuture future, + SocketAddress localAddress) { + try { + channel.socket.socket().bind(localAddress); + channel.boundManually = true; + future.setSuccess(); + fireChannelBound(channel, channel.getLocalAddress()); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + private void connect( + final NioClientSocketChannel channel, ChannelFuture future, + SocketAddress remoteAddress) { + try { + if (channel.socket.connect(remoteAddress)) { + NioWorker worker = nextWorker(); + channel.setWorker(worker); + worker.register(channel, future); + } else { + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + if (future.isCancelled()) { + channel.close(); + } + } + }); + channel.connectFuture = future; + boss.register(channel); + } + + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + NioWorker nextWorker() { + return workers[Math.abs( + workerIndex.getAndIncrement() % workers.length)]; + } + + private class Boss implements Runnable { + + private final AtomicBoolean started = new AtomicBoolean(); + private volatile Selector selector; + private final Object selectorGuard = new Object(); + + Boss() { + super(); + } + + void register(NioSocketChannel channel) { + boolean firstChannel = started.compareAndSet(false, true); + Selector selector; + if (firstChannel) { + try { + this.selector = selector = Selector.open(); + } catch (IOException e) { + throw new ChannelException( + "Failed to create a selector.", e); + } + } else { + selector = this.selector; + if (selector == null) { + do { + Thread.yield(); + selector = this.selector; + } while (selector == null); + } + } + + if (firstChannel) { + try { + channel.socket.register(selector, SelectionKey.OP_CONNECT, channel); + } catch (ClosedChannelException e) { + throw new ChannelException( + "Failed to register a socket to the selector.", e); + } + bossExecutor.execute(new NamePreservingRunnable( + this, + "New I/O client boss #" + id)); + } else { + synchronized (selectorGuard) { + selector.wakeup(); + try { + channel.socket.register(selector, SelectionKey.OP_CONNECT, channel); + } catch (ClosedChannelException e) { + throw new ChannelException( + "Failed to register a socket to the selector.", e); + } + } + } + } + + public void run() { + boolean shutdown = false; + Selector selector = this.selector; + for (;;) { + synchronized (selectorGuard) { + // This empty synchronization block prevents the selector + // from acquiring its lock. + } + try { + int selectedKeyCount = selector.select(500); + if (selectedKeyCount > 0) { + processSelectedKeys(selector.selectedKeys()); + } + + // Exit the loop when there's nothing to handle. + // The shutdown flag is used to delay the shutdown of this + // loop to avoid excessive Selector creation when + // connection attempts are made in a one-by-one manner + // instead of concurrent manner. + if (selector.keys().isEmpty()) { + if (shutdown) { + synchronized (selectorGuard) { + if (selector.keys().isEmpty()) { + try { + selector.close(); + } catch (IOException e) { + logger.warn( + "Failed to close a selector.", + e); + } finally { + this.selector = null; + } + started.set(false); + break; + } else { + shutdown = false; + } + } + } else { + // Give one more second. + shutdown = true; + } + } else { + shutdown = false; + } + } catch (Throwable t) { + logger.warn( + "Unexpected exception in the selector loop.", t); + + // Prevent possible consecutive immediate failures. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore. + } + } + } + } + + private void processSelectedKeys(Set selectedKeys) { + for (Iterator i = selectedKeys.iterator(); i.hasNext();) { + SelectionKey k = i.next(); + i.remove(); + + if (!k.isValid()) { + close(k); + continue; + } + + if (k.isConnectable()) { + connect(k); + } + } + } + + private void connect(SelectionKey k) { + NioClientSocketChannel ch = (NioClientSocketChannel) k.attachment(); + try { + if (ch.socket.finishConnect()) { + k.cancel(); + NioWorker worker = nextWorker(); + ch.setWorker(worker); + worker.register(ch, ch.connectFuture); + } + } catch (Throwable t) { + k.cancel(); + ch.connectFuture.setFailure(t); + fireExceptionCaught(ch, t); + close(k); + } + } + + private void close(SelectionKey k) { + NioSocketChannel ch = (NioSocketChannel) k.attachment(); + NioWorker.close(ch, ch.getSucceededFuture()); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannel.java new file mode 100644 index 000000000000..3048f1a4c2b9 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannel.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.channels.ServerSocketChannel; + +import net.gleamynode.netty.channel.AbstractServerChannel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.DefaultServerSocketChannelConfig; +import net.gleamynode.netty.channel.socket.ServerSocketChannelConfig; +import net.gleamynode.netty.logging.Logger; + +class NioServerSocketChannel extends AbstractServerChannel + implements net.gleamynode.netty.channel.socket.ServerSocketChannel { + + private static final Logger logger = + Logger.getLogger(NioServerSocketChannel.class); + + final ServerSocketChannel socket; + private final ServerSocketChannelConfig config; + + NioServerSocketChannel( + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink) { + + super(factory, pipeline, sink); + + try { + socket = ServerSocketChannel.open(); + } catch (IOException e) { + throw new ChannelException( + "Failed to open a server socket.", e); + } + + try { + socket.socket().setSoTimeout(1000); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { + logger.warn( + "Failed to close a partially initialized socket.", e2); + } + throw new ChannelException( + "Failed to set the server socket timeout.", e); + } + + config = new DefaultServerSocketChannelConfig(socket.socket()); + + fireChannelOpen(this); + } + + public ServerSocketChannelConfig getConfig() { + return config; + } + + public InetSocketAddress getLocalAddress() { + return (InetSocketAddress) socket.socket().getLocalSocketAddress(); + } + + public InetSocketAddress getRemoteAddress() { + return null; + } + + public boolean isBound() { + return isOpen() && socket.socket().isBound(); + } + + public boolean isConnected() { + return false; + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + @Override + protected ChannelFuture getSucceededFuture() { + return super.getSucceededFuture(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannelFactory.java new file mode 100644 index 000000000000..a21ee30d8ba2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketChannelFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.ServerSocketChannel; +import net.gleamynode.netty.channel.socket.ServerSocketChannelFactory; + +public class NioServerSocketChannelFactory implements ServerSocketChannelFactory { + + final Executor bossExecutor; + private final ChannelSink sink; + + public NioServerSocketChannelFactory( + Executor bossExecutor, Executor workerExecutor) { + this(bossExecutor, workerExecutor, Runtime.getRuntime().availableProcessors()); + } + + public NioServerSocketChannelFactory( + Executor bossExecutor, Executor workerExecutor, + int workerCount) { + if (bossExecutor == null) { + throw new NullPointerException("bossExecutor"); + } + if (workerExecutor == null) { + throw new NullPointerException("workerExecutor"); + } + if (workerCount <= 0) { + throw new IllegalArgumentException( + "workerCount (" + workerCount + ") " + + "must be a positive integer."); + } + this.bossExecutor = bossExecutor; + sink = new NioServerSocketPipelineSink(workerExecutor, workerCount); + } + + public ServerSocketChannel newChannel(ChannelPipeline pipeline) { + return new NioServerSocketChannel(this, pipeline, sink); + } + +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketPipelineSink.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketPipelineSink.java new file mode 100644 index 000000000000..9b8cc0e772bc --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioServerSocketPipelineSink.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SocketChannel; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; + +import net.gleamynode.netty.channel.AbstractChannelSink; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.logging.Logger; +import net.gleamynode.netty.util.NamePreservingRunnable; + +class NioServerSocketPipelineSink extends AbstractChannelSink { + + static final Logger logger = + Logger.getLogger(NioServerSocketPipelineSink.class); + private static final AtomicInteger nextId = new AtomicInteger(); + + private final int id = nextId.incrementAndGet(); + private final NioWorker[] workers; + private final AtomicInteger workerIndex = new AtomicInteger(); + + NioServerSocketPipelineSink(Executor workerExecutor, int workerCount) { + workers = new NioWorker[workerCount]; + for (int i = 0; i < workers.length; i ++) { + workers[i] = new NioWorker(id, i + 1, workerExecutor); + } + } + + public void eventSunk( + ChannelPipeline pipeline, ChannelEvent e) throws Exception { + Channel channel = e.getChannel(); + if (channel instanceof NioServerSocketChannel) { + handleServerSocket(e); + } else if (channel instanceof NioSocketChannel) { + handleAcceptedSocket(e); + } + } + + private void handleServerSocket(ChannelEvent e) { + if (!(e instanceof ChannelStateEvent)) { + return; + } + + ChannelStateEvent event = (ChannelStateEvent) e; + NioServerSocketChannel channel = + (NioServerSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + ChannelState state = event.getState(); + Object value = event.getValue(); + + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + close(channel, future); + } + break; + case BOUND: + if (value != null) { + bind(channel, future, (SocketAddress) value); + } else { + close(channel, future); + } + break; + } + } + + private void handleAcceptedSocket(ChannelEvent e) { + if (e instanceof ChannelStateEvent) { + ChannelStateEvent event = (ChannelStateEvent) e; + NioSocketChannel channel = (NioSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + ChannelState state = event.getState(); + Object value = event.getValue(); + + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + NioWorker.close(channel, future); + } + break; + case BOUND: + case CONNECTED: + if (value == null) { + NioWorker.close(channel, future); + } + break; + case INTEREST_OPS: + NioWorker.setInterestOps(channel, future, ((Integer) value).intValue()); + break; + } + } else if (e instanceof MessageEvent) { + MessageEvent event = (MessageEvent) e; + NioSocketChannel channel = (NioSocketChannel) event.getChannel(); + channel.writeBuffer.offer(event); + NioWorker.write(channel); + } + } + + private void bind( + NioServerSocketChannel channel, ChannelFuture future, + SocketAddress localAddress) { + + boolean bound = false; + boolean bossStarted = false; + try { + channel.socket.socket().bind(localAddress, channel.getConfig().getBacklog()); + bound = true; + + future.setSuccess(); + fireChannelBound(channel, channel.getLocalAddress()); + + Executor bossExecutor = + ((NioServerSocketChannelFactory) channel.getFactory()).bossExecutor; + bossExecutor.execute(new NamePreservingRunnable( + new Boss(channel), + "New I/O server boss #" + id +" (channelId: " + channel.getId() + + ", " + channel.getLocalAddress() + ')')); + bossStarted = true; + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } finally { + if (!bossStarted && bound) { + close(channel, future); + } + } + } + + private void close(NioServerSocketChannel channel, ChannelFuture future) { + boolean bound = channel.isBound(); + try { + channel.socket.close(); + future.setSuccess(); + if (channel.setClosed()) { + if (bound) { + fireChannelUnbound(channel); + } + fireChannelClosed(channel); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + NioWorker nextWorker() { + return workers[Math.abs( + workerIndex.getAndIncrement() % workers.length)]; + } + + private class Boss implements Runnable { + private final NioServerSocketChannel channel; + + Boss(NioServerSocketChannel channel) { + this.channel = channel; + } + + public void run() { + for (;;) { + try { + SocketChannel acceptedSocket = channel.socket.accept(); + try { + ChannelPipeline pipeline = + channel.getConfig().getPipelineFactory().getPipeline(); + NioWorker worker = nextWorker(); + worker.register(new NioAcceptedSocketChannel( + channel.getFactory(), pipeline, channel, + NioServerSocketPipelineSink.this, + acceptedSocket, worker), null); + } catch (Exception e) { + logger.warn( + "Failed to initialize an accepted socket.", e); + try { + acceptedSocket.close(); + } catch (IOException e2) { + logger.warn( + "Failed to close a partially accepted socket.", + e2); + } + } + } catch (SocketTimeoutException e) { + // Thrown every second to get ClosedChannelException + // raised. + } catch (ClosedChannelException e) { + // Closed as requested. + break; + } catch (IOException e) { + logger.warn( + "Failed to accept a connection.", e); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + // Ignore + } + } + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannel.java new file mode 100644 index 000000000000..09f2938215dc --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannel.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.SocketChannel; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import net.gleamynode.netty.channel.AbstractChannel; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +abstract class NioSocketChannel extends AbstractChannel + implements net.gleamynode.netty.channel.socket.SocketChannel { + + final SocketChannel socket; + private final NioSocketChannelConfig config; + + final Queue writeBuffer = + new ConcurrentLinkedQueue(); + MessageEvent currentWriteEvent; + int currentWriteIndex; + + public NioSocketChannel( + Channel parent, ChannelFactory factory, + ChannelPipeline pipeline, ChannelSink sink, + SocketChannel socket) { + super(parent, factory, pipeline, sink); + + this.socket = socket; + config = new DefaultNioSocketChannelConfig(socket.socket()); + } + + abstract NioWorker getWorker(); + abstract void setWorker(NioWorker worker); + + public NioSocketChannelConfig getConfig() { + return config; + } + + public InetSocketAddress getLocalAddress() { + return (InetSocketAddress) socket.socket().getLocalSocketAddress(); + } + + public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) socket.socket().getRemoteSocketAddress(); + } + + public boolean isBound() { + return isOpen() && socket.socket().isBound(); + } + + public boolean isConnected() { + return isOpen() && socket.socket().isConnected(); + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + @Override + protected void setInterestOpsNow(int interestOps) { + super.setInterestOpsNow(interestOps); + } + + @Override + protected ChannelFuture getSucceededFuture() { + return super.getSucceededFuture(); + } + + @Override + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { + return super.write(message, null); + } else { + return getUnsupportedOperationFuture(); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannelConfig.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannelConfig.java new file mode 100644 index 000000000000..ebf81693173e --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioSocketChannelConfig.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; + +import net.gleamynode.netty.channel.socket.SocketChannelConfig; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.has net.gleamynode.netty.channel.socket.nio.ReceiveBufferSizePredictor + */ +public interface NioSocketChannelConfig extends SocketChannelConfig { + + int getWriteSpinCount(); + + /** + * The maximum loop count for a write operation until + * {@link WritableByteChannel#write(ByteBuffer)} returns a non-zero value. + * It is similar to what a spin lock is for in concurrency programming. + * It improves memory utilization and write throughput significantly. + */ + void setWriteSpinCount(int writeSpinCount); + + ReceiveBufferSizePredictor getReceiveBufferSizePredictor(); + void setReceiveBufferSizePredictor(ReceiveBufferSizePredictor predictor); + + boolean isReadWriteFair(); + void setReadWriteFair(boolean fair); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/NioWorker.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioWorker.java new file mode 100644 index 000000000000..31749742eb30 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/NioWorker.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.logging.Logger; +import net.gleamynode.netty.util.NamePreservingRunnable; + +class NioWorker implements Runnable { + + private static final Logger logger = Logger.getLogger(NioWorker.class); + + private final int bossId; + private final int id; + private final Executor executor; + private final AtomicBoolean started = new AtomicBoolean(); + volatile Thread thread; + volatile Selector selector; + final Object selectorGuard = new Object(); + + NioWorker(int bossId, int id, Executor executor) { + this.bossId = bossId; + this.id = id; + this.executor = executor; + } + + void register(NioSocketChannel channel, ChannelFuture future) { + boolean firstChannel = started.compareAndSet(false, true); + Selector selector; + if (firstChannel) { + try { + this.selector = selector = Selector.open(); + } catch (IOException e) { + throw new ChannelException( + "Failed to create a selector.", e); + } + } else { + selector = this.selector; + if (selector == null) { + do { + Thread.yield(); + selector = this.selector; + } while (selector == null); + } + } + + if (firstChannel) { + try { + channel.socket.register(selector, SelectionKey.OP_READ, channel); + if (future != null) { + future.setSuccess(); + } + } catch (ClosedChannelException e) { + future.setFailure(e); + throw new ChannelException( + "Failed to register a socket to the selector.", e); + } + + boolean server = !(channel instanceof NioClientSocketChannel); + if (server) { + fireChannelOpen(channel); + } + + fireChannelBound(channel, channel.getLocalAddress()); + fireChannelConnected(channel, channel.getRemoteAddress()); + + String threadName = + (server ? "New I/O server worker #" + : "New I/O client worker #") + bossId + '-' + id; + + executor.execute(new NamePreservingRunnable(this, threadName)); + } else { + synchronized (selectorGuard) { + selector.wakeup(); + try { + channel.socket.register(selector, SelectionKey.OP_READ, channel); + if (future != null) { + future.setSuccess(); + } + } catch (ClosedChannelException e) { + future.setFailure(e); + throw new ChannelException( + "Failed to register a socket to the selector.", e); + } + + fireChannelOpen(channel); + fireChannelBound(channel, channel.getLocalAddress()); + fireChannelConnected(channel, channel.getRemoteAddress()); + } + } + } + + public void run() { + thread = Thread.currentThread(); + + boolean shutdown = false; + Selector selector = this.selector; + for (;;) { + synchronized (selectorGuard) { + // This empty synchronization block prevents the selector + // from acquiring its lock. + } + try { + int selectedKeyCount = selector.select(500); + if (selectedKeyCount > 0) { + processSelectedKeys(selector.selectedKeys()); + } + + // Exit the loop when there's nothing to handle. + // The shutdown flag is used to delay the shutdown of this + // loop to avoid excessive Selector creation when + // connections are registered in a one-by-one manner instead of + // concurrent manner. + if (selector.keys().isEmpty()) { + if (shutdown) { + synchronized (selectorGuard) { + if (selector.keys().isEmpty()) { + try { + selector.close(); + } catch (IOException e) { + logger.warn( + "Failed to close a selector.", e); + } finally { + this.selector = null; + } + started.set(false); + break; + } else { + shutdown = false; + } + } + } else { + // Give one more second. + shutdown = true; + } + } else { + shutdown = false; + } + } catch (Throwable t) { + logger.warn( + "Unexpected exception in the selector loop.", t); + + // Prevent possible consecutive immediate failures. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore. + } + } + } + } + + private static void processSelectedKeys(Set selectedKeys) { + for (Iterator i = selectedKeys.iterator(); i.hasNext();) { + SelectionKey k = i.next(); + i.remove(); + + if (!k.isValid()) { + close(k); + continue; + } + + if (k.isReadable()) { + read(k); + } + + if (!k.isValid()) { + close(k); + continue; + } + + if (k.isWritable()) { + write(k); + } + } + } + + private static void read(SelectionKey k) { + ReadableByteChannel ch = (ReadableByteChannel) k.channel(); + NioSocketChannel channel = (NioSocketChannel) k.attachment(); + + ReceiveBufferSizePredictor predictor = + channel.getConfig().getReceiveBufferSizePredictor(); + ByteBuffer buf = ByteBuffer.allocate(predictor.nextReceiveBufferSize()); + + int ret = 0; + int readBytes = 0; + boolean failure = true; + try { + while ((ret = ch.read(buf)) > 0) { + readBytes += ret; + if (!buf.hasRemaining()) { + break; + } + } + failure = false; + } catch (Throwable t) { + fireExceptionCaught(channel, t); + } + + if (readBytes > 0) { + // Update the predictor. + predictor.previousReceiveBufferSize(readBytes); + + // Fire the event. + ChannelBuffer buffer; + if (readBytes == buf.capacity()) { + buffer = ChannelBuffers.wrappedBuffer(buf.array()); + } else { + buffer = ChannelBuffers.wrappedBuffer(buf.array(), 0, readBytes); + } + fireMessageReceived(channel, buffer); + } + + if (ret < 0 || failure) { + close(k); + } + } + + private static void write(SelectionKey k) { + NioSocketChannel ch = (NioSocketChannel) k.attachment(); + write(ch); + } + + private static void close(SelectionKey k) { + NioSocketChannel ch = (NioSocketChannel) k.attachment(); + close(ch, ch.getSucceededFuture()); + } + + static void write(NioSocketChannel channel) { + if (channel.writeBuffer.isEmpty() && channel.currentWriteEvent == null) { + return; + } + + boolean addOpWrite = false; + boolean removeOpWrite = false; + + final int maxWrittenBytes; + if (channel.getConfig().isReadWriteFair()) { + // Set limitation for the number of written bytes for read-write + // fairness. I used maxReadBufferSize * 3 / 2, which yields best + // performance in my experience while not breaking fairness much. + int previousReceiveBufferSize = + channel.getConfig().getReceiveBufferSizePredictor().nextReceiveBufferSize(); + maxWrittenBytes = previousReceiveBufferSize + previousReceiveBufferSize >>> 1; + } else { + maxWrittenBytes = Integer.MAX_VALUE; + } + int writtenBytes = 0; + + synchronized (channel.writeBuffer) { + for (;;) { + if (channel.writeBuffer.isEmpty() && channel.currentWriteEvent == null) { + removeOpWrite = true; + break; + } + + ChannelBuffer a; + if (channel.currentWriteEvent == null) { + channel.currentWriteEvent = channel.writeBuffer.poll(); + a = (ChannelBuffer) channel.currentWriteEvent.getMessage(); + channel.currentWriteIndex = a.readerIndex(); + } else { + a = (ChannelBuffer) channel.currentWriteEvent.getMessage(); + } + + int localWrittenBytes = 0; + try { + for (int i = channel.getConfig().getWriteSpinCount(); i > 0; i --) { + localWrittenBytes = a.getBytes( + channel.currentWriteIndex, + channel.socket, + Math.min(maxWrittenBytes - writtenBytes, a.writerIndex() - channel.currentWriteIndex)); + if (localWrittenBytes != 0) { + break; + } + } + } catch (Throwable t) { + channel.currentWriteEvent.getFuture().setFailure(t); + fireExceptionCaught(channel, t); + } + + writtenBytes += localWrittenBytes; + channel.currentWriteIndex += localWrittenBytes; + if (channel.currentWriteIndex == a.writerIndex()) { + // Successful write - proceed to the next message. + channel.currentWriteEvent.getFuture().setSuccess(); + channel.currentWriteEvent = null; + } else { + // Not written fully - perhaps the kernel buffer is full. + addOpWrite = true; + break; + } + } + } + + if (addOpWrite) { + setOpWrite(channel, true); + } else if (removeOpWrite) { + setOpWrite(channel, false); + } + } + + private static void setOpWrite(NioSocketChannel channel, boolean opWrite) { + NioWorker worker = channel.getWorker(); + if (worker == null) { + IllegalStateException cause = + new IllegalStateException("Channel not connected yet (null worker)"); + fireExceptionCaught(channel, cause); + return; + } + + Selector selector = worker.selector; + SelectionKey key = channel.socket.keyFor(selector); + if (!key.isValid()) { + close(key); + return; + } + int interestOps; + boolean changed = false; + if (opWrite) { + if (Thread.currentThread() == worker.thread) { + interestOps = key.interestOps(); + if ((interestOps & SelectionKey.OP_WRITE) == 0) { + interestOps |= SelectionKey.OP_WRITE; + key.interestOps(interestOps); + changed = true; + } + } else { + synchronized (worker.selectorGuard) { + selector.wakeup(); + interestOps = key.interestOps(); + if ((interestOps & SelectionKey.OP_WRITE) == 0) { + interestOps |= SelectionKey.OP_WRITE; + key.interestOps(interestOps); + changed = true; + } + } + } + } else { + if (Thread.currentThread() == worker.thread) { + interestOps = key.interestOps(); + if ((interestOps & SelectionKey.OP_WRITE) != 0) { + interestOps &= ~SelectionKey.OP_WRITE; + key.interestOps(interestOps); + changed = true; + } + } else { + synchronized (worker.selectorGuard) { + selector.wakeup(); + interestOps = key.interestOps(); + if ((interestOps & SelectionKey.OP_WRITE) != 0) { + interestOps &= ~SelectionKey.OP_WRITE; + key.interestOps(interestOps); + changed = true; + } + } + } + } + + if (changed) { + channel.setInterestOpsNow(interestOps); + fireChannelInterestChanged(channel, interestOps); + } + } + + static void close(NioSocketChannel channel, ChannelFuture future) { + NioWorker worker = channel.getWorker(); + if (worker != null) { + Selector selector = worker.selector; + SelectionKey key = channel.socket.keyFor(selector); + if (key != null) { + key.cancel(); + } + } + + boolean connected = channel.isConnected(); + boolean bound = channel.isBound(); + try { + channel.socket.close(); + future.setSuccess(); + if (channel.setClosed()) { + if (connected) { + if (channel.getInterestOps() != Channel.OP_WRITE) { + channel.setInterestOpsNow(Channel.OP_WRITE); + fireChannelInterestChanged(channel, Channel.OP_WRITE); + } + fireChannelDisconnected(channel); + } + if (bound) { + fireChannelUnbound(channel); + } + fireChannelClosed(channel); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + static void setInterestOps( + NioSocketChannel channel, ChannelFuture future, int interestOps) { + NioWorker worker = channel.getWorker(); + if (worker == null) { + IllegalStateException cause = + new IllegalStateException("Channel not connected yet (null worker)"); + future.setFailure(cause); + fireExceptionCaught(channel, cause); + return; + } + + Selector selector = worker.selector; + SelectionKey key = channel.socket.keyFor(selector); + if (key == null || selector == null) { + IllegalStateException cause = + new IllegalStateException("Channel not connected yet (SelectionKey not found)"); + future.setFailure(cause); + fireExceptionCaught(channel, cause); + } + + boolean changed = false; + try { + if (Thread.currentThread() == worker.thread) { + if (key.interestOps() != interestOps) { + key.interestOps(interestOps); + changed = true; + } + } else { + synchronized (worker.selectorGuard) { + selector.wakeup(); + if (key.interestOps() != interestOps) { + key.interestOps(interestOps); + changed = true; + } + } + } + + future.setSuccess(); + if (changed) { + channel.setInterestOpsNow(interestOps); + fireChannelInterestChanged(channel, interestOps); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/ReceiveBufferSizePredictor.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/ReceiveBufferSizePredictor.java new file mode 100644 index 000000000000..2f55db9e5671 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/ReceiveBufferSizePredictor.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.nio; + +public interface ReceiveBufferSizePredictor { + int nextReceiveBufferSize(); + void previousReceiveBufferSize(int previousReceiveBufferSize); +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/nio/package-info.java b/src/main/java/net/gleamynode/netty/channel/socket/nio/package-info.java new file mode 100644 index 000000000000..16b5ba784398 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/nio/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * NIO-based socket channel + * API implementation - recommended for large number of connections (> 1000). + */ +package net.gleamynode.netty.channel.socket.nio; diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioAcceptedSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioAcceptedSocketChannel.java new file mode 100644 index 000000000000..5fb3002ba6ec --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioAcceptedSocketChannel.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.Socket; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; + +class OioAcceptedSocketChannel extends OioSocketChannel { + + private final PushbackInputStream in; + private final OutputStream out; + + OioAcceptedSocketChannel( + Channel parent, + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink, + Socket socket) { + + super(parent, factory, pipeline, sink, socket); + + try { + in = new PushbackInputStream(socket.getInputStream(), 1); + } catch (IOException e) { + throw new ChannelException("Failed to obtain an InputStream.", e); + } + try { + out = socket.getOutputStream(); + } catch (IOException e) { + throw new ChannelException("Failed to obtain an OutputStream.", e); + } + + fireChannelOpen(this); + fireChannelBound(this, getLocalAddress()); + fireChannelConnected(this, getRemoteAddress()); + } + + @Override + PushbackInputStream getInputStream() { + return in; + } + + @Override + OutputStream getOutputStream() { + return out; + } + + @Override + void setInputStream(PushbackInputStream in) { + if (this.in != in) { + throw new IllegalStateException("Should not reach here."); + } + } + + @Override + void setOutputStream(OutputStream out) { + if (this.out != out) { + throw new IllegalStateException("Should not reach here."); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannel.java new file mode 100644 index 000000000000..0ca412d44926 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannel.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.Socket; + +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; + +class OioClientSocketChannel extends OioSocketChannel { + + volatile PushbackInputStream in; + volatile OutputStream out; + + OioClientSocketChannel( + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink) { + + super(null, factory, pipeline, sink, new Socket()); + + fireChannelOpen(this); + } + + @Override + PushbackInputStream getInputStream() { + return in; + } + + @Override + OutputStream getOutputStream() { + return out; + } + + @Override + void setInputStream(PushbackInputStream in) { + if (this.in == null) { + this.in = in; + } else if (this.in != in) { + throw new IllegalStateException("Shouldn't reach here."); + } + } + + @Override + void setOutputStream(OutputStream out) { + if (this.out == null) { + this.out = out; + } else if (this.out != out) { + throw new IllegalStateException("Shouldn't reach here."); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannelFactory.java new file mode 100644 index 000000000000..efaa278b0c29 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketChannelFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.socket.ClientSocketChannelFactory; +import net.gleamynode.netty.channel.socket.SocketChannel; + +public class OioClientSocketChannelFactory implements ClientSocketChannelFactory { + + final OioClientSocketPipelineSink sink; + + public OioClientSocketChannelFactory(Executor workerExecutor) { + if (workerExecutor == null) { + throw new NullPointerException("workerExecutor"); + } + sink = new OioClientSocketPipelineSink(workerExecutor); + } + + public SocketChannel newChannel(ChannelPipeline pipeline) { + return new OioClientSocketChannel(this, pipeline, sink); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketPipelineSink.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketPipelineSink.java new file mode 100644 index 000000000000..dbebfec8a242 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioClientSocketPipelineSink.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.PushbackInputStream; +import java.net.SocketAddress; +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.AbstractChannelSink; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.util.NamePreservingRunnable; + +class OioClientSocketPipelineSink extends AbstractChannelSink { + + private final Executor workerExecutor; + + OioClientSocketPipelineSink(Executor workerExecutor) { + this.workerExecutor = workerExecutor; + } + + public void eventSunk( + ChannelPipeline pipeline, ChannelEvent e) throws Exception { + OioClientSocketChannel channel = (OioClientSocketChannel) e.getChannel(); + ChannelFuture future = e.getFuture(); + if (e instanceof ChannelStateEvent) { + ChannelStateEvent stateEvent = (ChannelStateEvent) e; + ChannelState state = stateEvent.getState(); + Object value = stateEvent.getValue(); + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + OioWorker.close(channel, future); + } + break; + case BOUND: + if (value != null) { + bind(channel, future, (SocketAddress) value); + } else { + OioWorker.close(channel, future); + } + break; + case CONNECTED: + if (value != null) { + connect(channel, future, (SocketAddress) value); + } else { + OioWorker.close(channel, future); + } + break; + case INTEREST_OPS: + OioWorker.setInterestOps(channel, future, ((Integer) value).intValue()); + break; + } + } else if (e instanceof MessageEvent) { + OioWorker.write( + channel, future, + ((MessageEvent) e).getMessage()); + } + } + + private void bind( + OioClientSocketChannel channel, ChannelFuture future, + SocketAddress localAddress) { + try { + channel.socket.bind(localAddress); + future.setSuccess(); + fireChannelBound(channel, channel.getLocalAddress()); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + private void connect( + OioClientSocketChannel channel, ChannelFuture future, + SocketAddress remoteAddress) { + + boolean bound = channel.isBound(); + boolean connected = false; + boolean workerStarted = false; + + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + if (future.isCancelled()) { + future.getChannel().close(); + } + } + }); + + try { + channel.socket.connect( + remoteAddress, channel.getConfig().getConnectTimeoutMillis()); + connected = true; + + // Obtain I/O stream. + channel.in = new PushbackInputStream(channel.socket.getInputStream(), 1); + channel.out = channel.socket.getOutputStream(); + + // Fire events. + future.setSuccess(); + if (!bound) { + fireChannelBound(channel, channel.getLocalAddress()); + } + fireChannelConnected(channel, channel.getRemoteAddress()); + + // Start the business. + workerExecutor.execute(new NamePreservingRunnable( + new OioWorker(channel), + "Old I/O client worker (channelId: " + channel.getId() + ", " + + channel.getLocalAddress() + " => " + + channel.getRemoteAddress() + ')')); + + workerStarted = true; + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } finally { + if (connected && !workerStarted) { + OioWorker.close(channel, future); + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannel.java new file mode 100644 index 000000000000..0bbe79ec7897 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannel.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; + +import net.gleamynode.netty.channel.AbstractServerChannel; +import net.gleamynode.netty.channel.ChannelException; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.DefaultServerSocketChannelConfig; +import net.gleamynode.netty.channel.socket.ServerSocketChannel; +import net.gleamynode.netty.channel.socket.ServerSocketChannelConfig; +import net.gleamynode.netty.logging.Logger; + +class OioServerSocketChannel extends AbstractServerChannel + implements ServerSocketChannel { + + private static final Logger logger = + Logger.getLogger(OioServerSocketChannel.class); + + final ServerSocket socket; + private final ServerSocketChannelConfig config; + + OioServerSocketChannel( + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink) { + + super(factory, pipeline, sink); + + try { + socket = new ServerSocket(); + } catch (IOException e) { + throw new ChannelException( + "Failed to open a server socket.", e); + } + + try { + socket.setSoTimeout(1000); + } catch (IOException e) { + try { + socket.close(); + } catch (IOException e2) { + logger.warn( + "Failed to close a partially initialized socket.", e2); + } + throw new ChannelException( + "Failed to set the server socket timeout.", e); + } + + config = new DefaultServerSocketChannelConfig(socket); + + fireChannelOpen(this); + } + + public ServerSocketChannelConfig getConfig() { + return config; + } + + public InetSocketAddress getLocalAddress() { + return (InetSocketAddress) socket.getLocalSocketAddress(); + } + + public InetSocketAddress getRemoteAddress() { + return null; + } + + public boolean isBound() { + return isOpen() & socket.isBound(); + } + + public boolean isConnected() { + return false; + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + @Override + protected ChannelFuture getSucceededFuture() { + return super.getSucceededFuture(); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannelFactory.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannelFactory.java new file mode 100644 index 000000000000..edaf5736b2cb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketChannelFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.ServerSocketChannel; +import net.gleamynode.netty.channel.socket.ServerSocketChannelFactory; + +public class OioServerSocketChannelFactory implements ServerSocketChannelFactory { + + final Executor bossExecutor; + private final ChannelSink sink; + + public OioServerSocketChannelFactory( + Executor bossExecutor, Executor workerExecutor) { + if (bossExecutor == null) { + throw new NullPointerException("bossExecutor"); + } + if (workerExecutor == null) { + throw new NullPointerException("workerExecutor"); + } + this.bossExecutor = bossExecutor; + sink = new OioServerSocketPipelineSink(workerExecutor); + } + + public ServerSocketChannel newChannel(ChannelPipeline pipeline) { + return new OioServerSocketChannel(this, pipeline, sink); + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketPipelineSink.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketPipelineSink.java new file mode 100644 index 000000000000..a594a8195328 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioServerSocketPipelineSink.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.IOException; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.AbstractChannelSink; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.logging.Logger; +import net.gleamynode.netty.util.NamePreservingRunnable; + +class OioServerSocketPipelineSink extends AbstractChannelSink { + + static final Logger logger = + Logger.getLogger(OioServerSocketPipelineSink.class); + + final Executor workerExecutor; + + OioServerSocketPipelineSink(Executor workerExecutor) { + this.workerExecutor = workerExecutor; + } + + public void eventSunk( + ChannelPipeline pipeline, ChannelEvent e) throws Exception { + Channel channel = e.getChannel(); + if (channel instanceof OioServerSocketChannel) { + handleServerSocket(e); + } else if (channel instanceof OioAcceptedSocketChannel) { + handleAcceptedSocket(e); + } + } + + private void handleServerSocket(ChannelEvent e) { + if (!(e instanceof ChannelStateEvent)) { + return; + } + + ChannelStateEvent event = (ChannelStateEvent) e; + OioServerSocketChannel channel = + (OioServerSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + ChannelState state = event.getState(); + Object value = event.getValue(); + + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + close(channel, future); + } + break; + case BOUND: + if (value != null) { + bind(channel, future, (SocketAddress) value); + } else { + close(channel, future); + } + break; + } + } + + private void handleAcceptedSocket(ChannelEvent e) { + if (e instanceof ChannelStateEvent) { + ChannelStateEvent event = (ChannelStateEvent) e; + OioAcceptedSocketChannel channel = + (OioAcceptedSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + ChannelState state = event.getState(); + Object value = event.getValue(); + + switch (state) { + case OPEN: + if (Boolean.FALSE.equals(value)) { + OioWorker.close(channel, future); + } + break; + case BOUND: + case CONNECTED: + if (value == null) { + OioWorker.close(channel, future); + } + break; + case INTEREST_OPS: + OioWorker.setInterestOps(channel, future, ((Integer) value).intValue()); + break; + } + } else if (e instanceof MessageEvent) { + MessageEvent event = (MessageEvent) e; + OioSocketChannel channel = (OioSocketChannel) event.getChannel(); + ChannelFuture future = event.getFuture(); + Object message = event.getMessage(); + OioWorker.write(channel, future, message); + } + } + + private void bind( + OioServerSocketChannel channel, ChannelFuture future, + SocketAddress localAddress) { + + boolean bound = false; + boolean bossStarted = false; + try { + channel.socket.bind(localAddress, channel.getConfig().getBacklog()); + bound = true; + + future.setSuccess(); + localAddress = channel.getLocalAddress(); + fireChannelBound(channel, localAddress); + + Executor bossExecutor = + ((OioServerSocketChannelFactory) channel.getFactory()).bossExecutor; + bossExecutor.execute(new NamePreservingRunnable( + new Boss(channel), + "Old I/O server boss (channelId: " + channel.getId() + + ", " + localAddress + ')')); + bossStarted = true; + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } finally { + if (!bossStarted && bound) { + close(channel, future); + } + } + } + + private void close(OioServerSocketChannel channel, ChannelFuture future) { + boolean bound = channel.isBound(); + try { + channel.socket.close(); + future.setSuccess(); + if (channel.setClosed()) { + if (bound) { + fireChannelUnbound(channel); + } + fireChannelClosed(channel); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + private class Boss implements Runnable { + private final OioServerSocketChannel channel; + + Boss(OioServerSocketChannel channel) { + this.channel = channel; + } + + public void run() { + while (channel.isBound()) { + try { + Socket acceptedSocket = channel.socket.accept(); + try { + ChannelPipeline pipeline = + channel.getConfig().getPipelineFactory().getPipeline(); + final OioAcceptedSocketChannel acceptedChannel = + new OioAcceptedSocketChannel( + channel, + channel.getFactory(), + pipeline, + OioServerSocketPipelineSink.this, + acceptedSocket); + workerExecutor.execute( + new NamePreservingRunnable( + new OioWorker(acceptedChannel), + "Old I/O server worker (parentId: " + + channel.getId() + + ", channelId: " + acceptedChannel.getId() + ", " + + channel.getRemoteAddress() + " => " + + channel.getLocalAddress() + ')')); + } catch (Exception e) { + logger.warn( + "Failed to initialize an accepted socket.", e); + try { + acceptedSocket.close(); + } catch (IOException e2) { + logger.warn( + "Failed to close a partially accepted socket.", + e2); + } + } + } catch (SocketTimeoutException e) { + // Thrown every second to stop when requested. + } catch (IOException e) { + logger.warn( + "Failed to accept a connection.", e); + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + // Ignore + } + } + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioSocketChannel.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioSocketChannel.java new file mode 100644 index 000000000000..91ba5d3d027c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioSocketChannel.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import java.io.OutputStream; +import java.io.PushbackInputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; + +import net.gleamynode.netty.channel.AbstractChannel; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelSink; +import net.gleamynode.netty.channel.socket.DefaultSocketChannelConfig; +import net.gleamynode.netty.channel.socket.SocketChannel; +import net.gleamynode.netty.channel.socket.SocketChannelConfig; + +abstract class OioSocketChannel extends AbstractChannel + implements SocketChannel { + + final Socket socket; + private final SocketChannelConfig config; + volatile Thread workerThread; + + OioSocketChannel( + Channel parent, + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink, + Socket socket) { + + super(parent, factory, pipeline, sink); + + this.socket = socket; + config = new DefaultSocketChannelConfig(socket); + } + + public SocketChannelConfig getConfig() { + return config; + } + + public InetSocketAddress getLocalAddress() { + return (InetSocketAddress) socket.getLocalSocketAddress(); + } + + public InetSocketAddress getRemoteAddress() { + return (InetSocketAddress) socket.getRemoteSocketAddress(); + } + + public boolean isBound() { + return isOpen() & socket.isBound(); + } + + public boolean isConnected() { + return isOpen() & socket.isConnected(); + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + @Override + protected void setInterestOpsNow(int interestOps) { + super.setInterestOpsNow(interestOps); + } + + @Override + protected ChannelFuture getSucceededFuture() { + return super.getSucceededFuture(); + } + + abstract PushbackInputStream getInputStream(); + abstract void setInputStream(PushbackInputStream in); + abstract OutputStream getOutputStream(); + abstract void setOutputStream(OutputStream out); + + @Override + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { + return super.write(message, null); + } else { + return getUnsupportedOperationFuture(); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/OioWorker.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioWorker.java new file mode 100644 index 000000000000..50969ba0118f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/OioWorker.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.channel.socket.oio; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.OutputStream; +import java.io.PushbackInputStream; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelFuture; + +class OioWorker implements Runnable { + + private final OioSocketChannel channel; + + OioWorker(OioSocketChannel channel) { + this.channel = channel; + } + + public void run() { + channel.workerThread = Thread.currentThread(); + final PushbackInputStream in = channel.getInputStream(); + + for (;;) { + synchronized (this) { + while (!channel.isReadable()) { + try { + this.wait(); + } catch (InterruptedException e) { + if (!channel.isOpen()) { + break; + } + } + } + } + + byte[] buf; + int readBytes; + try { + int bytesToRead = in.available(); + if (bytesToRead > 0) { + buf = new byte[bytesToRead]; + readBytes = in.read(buf); + } else { + int b = in.read(); + if (b < 0) { + break; + } + in.unread(b); + continue; + } + } catch (Throwable t) { + if (!channel.socket.isClosed()) { + fireExceptionCaught(channel, t); + } + break; + } + + ChannelBuffer buffer; + if (readBytes == buf.length) { + buffer = ChannelBuffers.wrappedBuffer(buf); + } else { + buffer = ChannelBuffers.wrappedBuffer(buf, 0, readBytes); + } + fireMessageReceived(channel, buffer); + } + close(channel, channel.getSucceededFuture()); + } + + static void write( + OioSocketChannel channel, ChannelFuture future, + Object message) { + OutputStream out = channel.getOutputStream(); + try { + ChannelBuffer a = (ChannelBuffer) message; + synchronized (out) { + a.getBytes(a.readerIndex(), out, a.readableBytes()); + } + future.setSuccess(); + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + static void setInterestOps( + OioSocketChannel channel, ChannelFuture future, int interestOps) { + + boolean changed = false; + try { + if (channel.getInterestOps() != interestOps) { + if ((interestOps & Channel.OP_READ) != 0) { + channel.setInterestOpsNow(Channel.OP_READ); + } else { + channel.setInterestOpsNow(Channel.OP_NONE); + } + changed = true; + } + + future.setSuccess(); + if (changed) { + // Notify the worker so it stops reading. + Thread currentThread = Thread.currentThread(); + Thread workerThread = channel.workerThread; + if (workerThread != null && currentThread != workerThread) { + workerThread.interrupt(); + } + + channel.setInterestOpsNow(interestOps); + fireChannelInterestChanged(channel, interestOps); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } + + static void close(OioSocketChannel channel, ChannelFuture future) { + boolean connected = channel.isConnected(); + boolean bound = channel.isBound(); + try { + channel.socket.close(); + future.setSuccess(); + if (channel.setClosed()) { + if (connected) { + if (channel.getInterestOps() != Channel.OP_WRITE) { + channel.setInterestOpsNow(Channel.OP_WRITE); + fireChannelInterestChanged(channel, Channel.OP_WRITE); + } + fireChannelDisconnected(channel); + } + if (bound) { + fireChannelUnbound(channel); + } + fireChannelClosed(channel); + } + } catch (Throwable t) { + future.setFailure(t); + fireExceptionCaught(channel, t); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/channel/socket/oio/package-info.java b/src/main/java/net/gleamynode/netty/channel/socket/oio/package-info.java new file mode 100644 index 000000000000..01d343984ae5 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/oio/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Old blocking I/O based socket channel API implementation - recommended for + * small number of connections (< 1000). + */ +package net.gleamynode.netty.channel.socket.oio; diff --git a/src/main/java/net/gleamynode/netty/channel/socket/package-info.java b/src/main/java/net/gleamynode/netty/channel/socket/package-info.java new file mode 100644 index 000000000000..525cd21b97cd --- /dev/null +++ b/src/main/java/net/gleamynode/netty/channel/socket/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Abstract TCP and UDP socket interfaces which extend the core channel API. + */ +package net.gleamynode.netty.channel.socket; diff --git a/src/main/java/net/gleamynode/netty/example/discard/DiscardClient.java b/src/main/java/net/gleamynode/netty/example/discard/DiscardClient.java new file mode 100644 index 000000000000..b240ef4e90bb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/discard/DiscardClient.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.discard; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class DiscardClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length < 2 || args.length > 3) { + System.err.println( + "Usage: " + DiscardClient.class.getSimpleName() + + " []"); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + int firstMessageSize; + + if (args.length == 3) { + firstMessageSize = Integer.parseInt(args[2]); + } else { + firstMessageSize = 256; + } + + // Start client. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + DiscardHandler handler = new DiscardHandler(firstMessageSize); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + bootstrap.connect(new InetSocketAddress(host, port)); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/discard/DiscardHandler.java b/src/main/java/net/gleamynode/netty/example/discard/DiscardHandler.java new file mode 100644 index 000000000000..f3d5f71a684f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/discard/DiscardHandler.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.discard; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("all") +public class DiscardHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + DiscardHandler.class.getName()); + + private final Random random = new Random(); + private final int messageSize; + private final AtomicLong transferredBytes = new AtomicLong(); + + public DiscardHandler() { + this(0); + } + + public DiscardHandler(int messageSize) { + if (messageSize < 0) { + throw new IllegalArgumentException( + "messageSize: " + messageSize); + } + this.messageSize = messageSize; + } + + public long getTransferredBytes() { + return transferredBytes.get(); + } + + @Override + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { + generateTraffic(e); + } + + @Override + public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) { + generateTraffic(e); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { + transferredBytes.addAndGet(((ChannelBuffer) e.getMessage()).readableBytes()); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } + + private void generateTraffic(ChannelStateEvent e) { + Channel channel = e.getChannel(); + while (channel.isWritable()) { + ChannelBuffer m = nextMessage(); + if (m == null) { + break; + } + channel.write(m); + } + } + + private ChannelBuffer nextMessage() { + if (messageSize == 0) { + return null; + } + + byte[] content = new byte[messageSize]; + random.nextBytes(content); + return ChannelBuffers.wrappedBuffer(content); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/discard/DiscardServer.java b/src/main/java/net/gleamynode/netty/example/discard/DiscardServer.java new file mode 100644 index 000000000000..acd7b8a24a12 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/discard/DiscardServer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.discard; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class DiscardServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + DiscardHandler handler = new DiscardHandler(); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + + // Start performance monitor. + new ThroughputMonitor(handler).start(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/discard/ThroughputMonitor.java b/src/main/java/net/gleamynode/netty/example/discard/ThroughputMonitor.java new file mode 100644 index 000000000000..bdb0437bfb12 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/discard/ThroughputMonitor.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.discard; + +public class ThroughputMonitor extends Thread { + + private final DiscardHandler echoHandler; + + public ThroughputMonitor(DiscardHandler echoHandler) { + this.echoHandler = echoHandler; + } + + @Override + public void run() { + long oldCounter = echoHandler.getTransferredBytes(); + long startTime = System.currentTimeMillis(); + for (;;) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + long endTime = System.currentTimeMillis(); + long newCounter = echoHandler.getTransferredBytes(); + System.err.format( + "%4.3f MiB/s%n", + (newCounter - oldCounter) * 1000 / (endTime - startTime) / + 1048576.0); + oldCounter = newCounter; + startTime = endTime; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/example/echo/EchoClient.java b/src/main/java/net/gleamynode/netty/example/echo/EchoClient.java new file mode 100644 index 000000000000..dda19ca199d5 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/echo/EchoClient.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.echo; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class EchoClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length < 2 || args.length > 3) { + System.err.println( + "Usage: " + EchoClient.class.getSimpleName() + + " []"); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + int firstMessageSize; + + if (args.length == 3) { + firstMessageSize = Integer.parseInt(args[2]); + } else { + firstMessageSize = 256; + } + + // Start client. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + EchoHandler handler = new EchoHandler(firstMessageSize); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + bootstrap.connect(new InetSocketAddress(host, port)); + + // Start performance monitor. + new ThroughputMonitor(handler).start(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/echo/EchoHandler.java b/src/main/java/net/gleamynode/netty/example/echo/EchoHandler.java new file mode 100644 index 000000000000..fda3dd47b0f3 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/echo/EchoHandler.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.echo; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("all") +public class EchoHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + EchoHandler.class.getName()); + + private final ChannelBuffer firstMessage; + private final AtomicLong transferredBytes = new AtomicLong(); + + public EchoHandler() { + this(0); + } + + public EchoHandler(int firstMessageSize) { + if (firstMessageSize < 0) { + throw new IllegalArgumentException( + "firstMessageSize: " + firstMessageSize); + } + firstMessage = ChannelBuffers.buffer(firstMessageSize); + for (int i = 0; i < firstMessage.capacity(); i ++) { + firstMessage.writeByte((byte) i); + } + } + + public long getTransferredBytes() { + return transferredBytes.get(); + } + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) { + e.getChannel().write(firstMessage); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + transferredBytes.addAndGet(((ChannelBuffer) e.getMessage()).readableBytes()); + e.getChannel().write(e.getMessage()); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/echo/EchoServer.java b/src/main/java/net/gleamynode/netty/example/echo/EchoServer.java new file mode 100644 index 000000000000..837d3d965ae5 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/echo/EchoServer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.echo; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class EchoServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + EchoHandler handler = new EchoHandler(); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + + // Start performance monitor. + new ThroughputMonitor(handler).start(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/echo/ThroughputMonitor.java b/src/main/java/net/gleamynode/netty/example/echo/ThroughputMonitor.java new file mode 100644 index 000000000000..e5190cd2b26f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/echo/ThroughputMonitor.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.echo; + +public class ThroughputMonitor extends Thread { + + private final EchoHandler echoHandler; + + public ThroughputMonitor(EchoHandler echoHandler) { + this.echoHandler = echoHandler; + } + + @Override + public void run() { + long oldCounter = echoHandler.getTransferredBytes(); + long startTime = System.currentTimeMillis(); + for (;;) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + long endTime = System.currentTimeMillis(); + long newCounter = echoHandler.getTransferredBytes(); + System.err.format( + "%4.3f MiB/s%n", + (newCounter - oldCounter) * 1000 / (endTime - startTime) / + 1048576.0); + oldCounter = newCounter; + startTime = endTime; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/BigIntegerDecoder.java b/src/main/java/net/gleamynode/netty/example/factorial/BigIntegerDecoder.java new file mode 100644 index 000000000000..7cce75e7abee --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/BigIntegerDecoder.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import java.math.BigInteger; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.handler.codec.frame.FrameDecoder; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class BigIntegerDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + if (buffer.readableBytes() < 4) { + return null; + } + int dataLength = buffer.getInt(buffer.readerIndex()); + + if (buffer.readableBytes() < dataLength + 4) { + return null; + } + + buffer.skipBytes(4); + + byte[] decoded = new byte[dataLength]; + buffer.readBytes(decoded); + + return new BigInteger(decoded); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialClient.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClient.java new file mode 100644 index 000000000000..e039c74057ca --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClient.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class FactorialClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length != 3) { + System.err.println( + "Usage: " + FactorialClient.class.getSimpleName() + + " "); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + int count = Integer.parseInt(args[2]); + if (count <= 0) { + throw new IllegalArgumentException("count must be a positive integer."); + } + + // Set up. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + + bootstrap.setPipelineFactory(new FactorialClientPipelineFactory(count)); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + // Make a new connection. + ChannelFuture connectFuture = + bootstrap.connect(new InetSocketAddress(host, port)); + + // Wait until the connection is made successfully. + // Get the handler instance to retrieve the answer. + FactorialClientHandler handler = (FactorialClientHandler) + connectFuture.await().getChannel().getPipeline().getLast(); + + // Print out the answer. + System.out.format( + "Factorial of %,d is: %,d", count, handler.getFactorial()); + + // We should shut down all thread pools here to exit normally. + // However, it's just fine to call System.exit(0) because we are + // finished with the business. + System.exit(0); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientHandler.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientHandler.java new file mode 100644 index 000000000000..48d6d6eb4710 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientHandler.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import java.math.BigInteger; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("one") +public class FactorialClientHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + FactorialClientHandler.class.getName()); + + private int i = 1; + private int receivedMessages = 0; + private final int count; + final BlockingQueue answer = new LinkedBlockingQueue(); + + public FactorialClientHandler(int count) { + this.count = count; + } + + public BigInteger getFactorial() { + for (;;) { + try { + return answer.take(); + } catch (InterruptedException e) { + // Ignore. + } + } + } + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) { + sendNumbers(e); + } + + @Override + public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) { + sendNumbers(e); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, final MessageEvent e) { + receivedMessages ++; + if (receivedMessages == count) { + // Offer the answer after closing the connection. + e.getChannel().close().addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) { + answer.offer((BigInteger) e.getMessage()); + } + }); + } + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } + + private void sendNumbers(ChannelStateEvent e) { + Channel channel = e.getChannel(); + while (channel.isWritable()) { + if (i <= count) { + channel.write(Integer.valueOf(i)); + i ++; + } else { + break; + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientPipelineFactory.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientPipelineFactory.java new file mode 100644 index 000000000000..7521ee9169ac --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialClientPipelineFactory.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import static net.gleamynode.netty.channel.Channels.*; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineFactory; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class FactorialClientPipelineFactory implements + ChannelPipelineFactory { + + private final int count; + + public FactorialClientPipelineFactory(int count) { + this.count = count; + } + + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + + // Add the number codec first, + pipeline.addLast("decoder", new BigIntegerDecoder()); + pipeline.addLast("encoder", new NumberEncoder()); + + // and then business logic. + pipeline.addLast("handler", new FactorialClientHandler(count)); + + return pipeline; + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialProtocolException.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialProtocolException.java new file mode 100644 index 000000000000..64bb14e793ff --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialProtocolException.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class FactorialProtocolException extends Exception { + + private static final long serialVersionUID = -7045872169845421748L; + + public FactorialProtocolException() { + super(); + } + + public FactorialProtocolException(String message, Throwable cause) { + super(message, cause); + } + + public FactorialProtocolException(String message) { + super(message); + } + + public FactorialProtocolException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialServer.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServer.java new file mode 100644 index 000000000000..d37f7a0f61ed --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServer.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class FactorialServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + + bootstrap.setPipelineFactory(new FactorialServerPipelineFactory()); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerHandler.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerHandler.java new file mode 100644 index 000000000000..7dfa89e74cf9 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerHandler.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import java.math.BigInteger; +import java.util.Formatter; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("one") +public class FactorialServerHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + FactorialServerHandler.class.getName()); + + private int lastMultiplier = 1; + private BigInteger factorial = new BigInteger(new byte[] { 1 }); + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + + // Calculate the cumulative factorial and send it to the client. + BigInteger number; + if (e.getMessage() instanceof BigInteger) { + number = (BigInteger) e.getMessage(); + } else { + number = new BigInteger(e.getMessage().toString()); + } + lastMultiplier = number.intValue(); + factorial = factorial.multiply(number); + e.getChannel().write(factorial); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + logger.info(new Formatter().format( + "Factorial of %,d is: %,d", lastMultiplier, factorial).toString()); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerPipelineFactory.java b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerPipelineFactory.java new file mode 100644 index 000000000000..6edc09620545 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/FactorialServerPipelineFactory.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import static net.gleamynode.netty.channel.Channels.*; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineFactory; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class FactorialServerPipelineFactory implements + ChannelPipelineFactory { + + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + + // Add the number codec first, + pipeline.addLast("decoder", new BigIntegerDecoder()); + pipeline.addLast("encoder", new NumberEncoder()); + + // and then business logic. + pipeline.addLast("handler", new FactorialServerHandler()); + + return pipeline; + } +} diff --git a/src/main/java/net/gleamynode/netty/example/factorial/NumberEncoder.java b/src/main/java/net/gleamynode/netty/example/factorial/NumberEncoder.java new file mode 100644 index 000000000000..61759c53ee3e --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/factorial/NumberEncoder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.factorial; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.math.BigInteger; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.ChannelDownstreamHandler; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +@ChannelPipelineCoverage("all") +public class NumberEncoder implements ChannelDownstreamHandler { + + public void handleDownstream( + ChannelHandlerContext ctx, ChannelEvent evt) throws Exception { + if (!(evt instanceof MessageEvent)) { + ctx.sendDownstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + if (!(e.getMessage() instanceof Number)) { + // Ignore what this encoder can't encode. + ctx.sendDownstream(evt); + return; + } + + // Convert to a BigInteger first for easier implementation. + BigInteger v; + if (e.getMessage() instanceof BigInteger) { + v = (BigInteger) e.getMessage(); + } else { + v = new BigInteger(e.getMessage().toString()); + } + + // Convert the number into a byte array. + byte[] data = v.toByteArray(); + int dataLength = data.length; + + // Construct a message with a length header. + ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); + buf.writeInt(dataLength); + buf.writeBytes(data); + + // Send the constructed message. + write(ctx, e.getChannel(), e.getFuture(), buf, e.getRemoteAddress()); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoClient.java b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoClient.java new file mode 100644 index 000000000000..d7128e7b9a08 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoClient.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.objectecho; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class ObjectEchoClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length < 2 || args.length > 3) { + System.err.println( + "Usage: " + ObjectEchoClient.class.getSimpleName() + + " []"); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + int firstMessageSize; + + if (args.length == 3) { + firstMessageSize = Integer.parseInt(args[2]); + } else { + firstMessageSize = 256; + } + + // Start client. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + ObjectEchoHandler handler = new ObjectEchoHandler(firstMessageSize); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + bootstrap.connect(new InetSocketAddress(host, port)); + + // Start performance monitor. + new ThroughputMonitor(handler).start(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoHandler.java b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoHandler.java new file mode 100644 index 000000000000..03c291727fe2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoHandler.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.objectecho; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; +import net.gleamynode.netty.handler.codec.serialization.ObjectDecoder; +import net.gleamynode.netty.handler.codec.serialization.ObjectEncoder; + +@ChannelPipelineCoverage("all") +public class ObjectEchoHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + ObjectEchoHandler.class.getName()); + + private final List firstMessage; + private final AtomicLong transferredMessages = new AtomicLong(); + + public ObjectEchoHandler() { + this(0); + } + + public ObjectEchoHandler(int firstMessageSize) { + if (firstMessageSize < 0) { + throw new IllegalArgumentException( + "firstMessageSize: " + firstMessageSize); + } + firstMessage = new ArrayList(firstMessageSize); + for (int i = 0; i < firstMessageSize; i ++) { + firstMessage.add(Integer.valueOf(i)); + } + } + + public long getTransferredMessages() { + return transferredMessages.get(); + } + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent && + ((ChannelStateEvent) e).getState() != ChannelState.INTEREST_OPS) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + e.getChannel().getPipeline().addFirst("encoder", new ObjectEncoder()); + e.getChannel().getPipeline().addFirst("decoder", new ObjectDecoder()); + } + + @Override + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) { + if (!firstMessage.isEmpty()) { + e.getChannel().write(firstMessage); + } + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + transferredMessages.incrementAndGet(); + e.getChannel().write(e.getMessage()); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoServer.java b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoServer.java new file mode 100644 index 000000000000..0979fce36fb2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/objectecho/ObjectEchoServer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.objectecho; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class ObjectEchoServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + ObjectEchoHandler handler = new ObjectEchoHandler(); + + bootstrap.getPipeline().addLast("handler", handler); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + + // Start performance monitor. + new ThroughputMonitor(handler).start(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/objectecho/ThroughputMonitor.java b/src/main/java/net/gleamynode/netty/example/objectecho/ThroughputMonitor.java new file mode 100644 index 000000000000..fff3ced9db3b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/objectecho/ThroughputMonitor.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.objectecho; + +public class ThroughputMonitor extends Thread { + + private final ObjectEchoHandler echoHandler; + + public ThroughputMonitor(ObjectEchoHandler echoHandler) { + this.echoHandler = echoHandler; + } + + @Override + public void run() { + long oldCounter = echoHandler.getTransferredMessages(); + long startTime = System.currentTimeMillis(); + for (;;) { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + long endTime = System.currentTimeMillis(); + long newCounter = echoHandler.getTransferredMessages(); + System.err.format( + "%4.3f msgs/s%n", + (newCounter - oldCounter) * 1000 / (double) (endTime - startTime)); + oldCounter = newCounter; + startTime = endTime; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClient.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClient.java new file mode 100644 index 000000000000..52ac97e9973d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClient.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class SecureChatClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length != 2) { + System.err.println( + "Usage: " + SecureChatClient.class.getSimpleName() + + " "); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + + // Start client. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + SecureChatClientHandler handler = new SecureChatClientHandler(); + + bootstrap.setPipelineFactory(new SecureChatPipelineFactory(handler)); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); + + // Wait until the connection attempt succeeds or fails. + Channel channel = future.awaitUninterruptibly().getChannel(); + if (!future.isSuccess()) { + future.getCause().printStackTrace(); + System.exit(0); + } + + // Read commands from the stdin. + ChannelFuture lastWriteFuture = null; + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + for (;;) { + String line = in.readLine(); + if (line == null) { + break; + } + lastWriteFuture = channel.write(line + '\n'); + } + + // Wait until all messages are flushed before closing the channel. + if (lastWriteFuture != null) { + lastWriteFuture.awaitUninterruptibly(); + } + + channel.close().awaitUninterruptibly(); + + // We should shut down all thread pools here to exit normally. + // However, it's just fine to call System.exit(0) because we are + // finished with the business. + System.exit(0); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClientHandler.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClientHandler.java new file mode 100644 index 000000000000..0f3d89da22c4 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatClientHandler.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; +import net.gleamynode.netty.handler.ssl.SslHandler; + +@ChannelPipelineCoverage("all") +public class SecureChatClientHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + SecureChatClientHandler.class.getName()); + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + + // Get the SslHandler and begin handshake ASAP. + SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class); + sslHandler.handshake(e.getChannel()); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + System.err.println(e.getMessage()); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatKeyStore.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatKeyStore.java new file mode 100644 index 000000000000..8cea8dd498cf --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatKeyStore.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SecureChatKeyStore { + private static final short[] DATA = new short[] { + 0xfe, 0xed, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5, + 0x27, 0x00, 0x00, 0x01, 0x9a, 0x30, 0x82, 0x01, + 0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05, + 0x00, 0x04, 0x82, 0x01, 0x82, 0x48, 0x6d, 0xcf, + 0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf, 0x47, 0x27, + 0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab, + 0x14, 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90, + 0x60, 0x7f, 0x12, 0x20, 0x56, 0xd1, 0x43, 0xa2, + 0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5, 0x83, + 0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad, + 0x8e, 0x8c, 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60, + 0x4b, 0xb4, 0x62, 0x82, 0x9e, 0x4a, 0x63, 0x83, + 0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60, + 0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6, + 0x92, 0x9e, 0x52, 0xc7, 0x7d, 0xbb, 0x35, 0x3b, + 0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99, 0x54, 0x97, + 0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e, + 0x6d, 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9, + 0xbd, 0x8f, 0xc5, 0x63, 0x25, 0x31, 0x20, 0x02, + 0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a, 0x10, + 0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77, + 0x46, 0xed, 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36, + 0x36, 0x33, 0x70, 0xb2, 0x63, 0x20, 0xca, 0x03, + 0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a, + 0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b, + 0x63, 0x82, 0x41, 0x65, 0x70, 0x37, 0x4b, 0x99, + 0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01, 0x95, 0x9f, + 0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68, + 0x40, 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13, + 0xe6, 0xa4, 0xcf, 0xd3, 0x67, 0xe3, 0xfd, 0x75, + 0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09, 0xac, + 0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c, + 0x0f, 0xf4, 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c, + 0x69, 0x98, 0x78, 0x3a, 0x25, 0xe4, 0xfd, 0x85, + 0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1, + 0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09, + 0x0a, 0xc3, 0x80, 0x2f, 0x10, 0x49, 0x89, 0x78, + 0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45, 0x91, 0x21, + 0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e, + 0x1c, 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54, + 0x60, 0xe7, 0x91, 0xfc, 0xd9, 0x3c, 0xe1, 0x6f, + 0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c, 0x39, + 0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9, + 0xf7, 0xa3, 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72, + 0xf2, 0xde, 0x96, 0xf2, 0xb1, 0x53, 0xb1, 0x3e, + 0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33, + 0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9, + 0xcb, 0xc7, 0x07, 0x41, 0xdd, 0x2f, 0xb6, 0x6a, + 0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2, 0x4b, 0xec, + 0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5, + 0x68, 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63, + 0xf7, 0x53, 0x99, 0x19, 0x68, 0x85, 0x66, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, + 0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82, + 0x02, 0x36, 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf1, + 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x30, 0x81, 0xa0, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, + 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30, + 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b, + 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, + 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68, + 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20, + 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, + 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, + 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, + 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, + 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, + 0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, + 0x31, 0x33, 0x38, 0x5a, 0x18, 0x0f, 0x32, 0x31, + 0x38, 0x37, 0x31, 0x31, 0x32, 0x34, 0x30, 0x35, + 0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, + 0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, + 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, + 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, + 0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31, 0x30, + 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, + 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74, + 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, + 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, + 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, + 0x00, 0xc3, 0xe3, 0x5e, 0x41, 0xa7, 0x87, 0x11, + 0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2, 0xe0, + 0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60, + 0x0b, 0x82, 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61, + 0xb8, 0x79, 0x3f, 0xb6, 0x3c, 0x12, 0x05, 0x93, + 0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8, + 0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41, + 0xf0, 0x2d, 0x28, 0xec, 0x06, 0xfb, 0xb4, 0x9f, + 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, + 0x65, 0x6c, 0x30, 0x01, 0xc2, 0x8e, 0x3e, 0xcb, + 0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a, 0x40, + 0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2, + 0xfd, 0xe2, 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22, + 0x04, 0xdd, 0x2c, 0x20, 0xc4, 0xfc, 0xdd, 0xd0, + 0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c, + 0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d, + 0xa2, 0x81, 0xba, 0x77, 0x9f, 0x2a, 0xd9, 0x44, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x6d, 0x79, + 0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f, + 0x5b, 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30, + 0x82, 0x01, 0x95, 0x30, 0x0e, 0x06, 0x0a, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29, + 0xa8, 0xb6, 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd, + 0xb5, 0xe5, 0x1a, 0x87, 0x68, 0xd1, 0x90, 0x4b, + 0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0, + 0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36, + 0x9a, 0xb2, 0x54, 0x77, 0x81, 0x07, 0x4b, 0xaa, + 0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5, 0x7c, 0x4e, + 0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65, + 0x03, 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5, + 0x3b, 0x92, 0xe4, 0x14, 0x05, 0x7a, 0x6a, 0x92, + 0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32, 0x04, + 0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99, + 0x30, 0x31, 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13, + 0xbf, 0xbe, 0xd0, 0x31, 0x49, 0xe7, 0x3c, 0xbf, + 0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9, + 0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91, + 0xd3, 0xfd, 0x24, 0x47, 0x0b, 0xe5, 0x53, 0xad, + 0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c, 0x37, 0xa8, + 0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab, + 0xbd, 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0, + 0xad, 0x7d, 0x50, 0x74, 0xf1, 0x98, 0x78, 0xbc, + 0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b, 0x81, + 0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b, + 0x5e, 0xc9, 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4, + 0xd8, 0x0b, 0x47, 0xd8, 0xfd, 0xd8, 0xe0, 0xed, + 0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6, + 0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13, + 0xc6, 0x0e, 0x6e, 0x74, 0xe0, 0x22, 0xac, 0xce, + 0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03, 0x74, 0x2b, + 0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82, + 0x3f, 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29, + 0x3b, 0xc1, 0x10, 0x7c, 0xd5, 0x77, 0x17, 0x79, + 0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd, 0x90, + 0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4, + 0x0b, 0x4f, 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64, + 0x40, 0xe1, 0x44, 0xc4, 0xf9, 0x40, 0x2b, 0x3b, + 0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4, + 0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15, + 0x0e, 0x3a, 0x25, 0x2a, 0x11, 0xee, 0x78, 0x2f, + 0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a, 0xb5, 0xb9, + 0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77, + 0x36, 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82, + 0xc3, 0x52, 0x58, 0xc4, 0x26, 0x39, 0xf3, 0xb3, + 0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e, 0xba, + 0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77, + 0x73, 0xa6, 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf, + 0x6d, 0xb4, 0x98, 0x8a, 0x18, 0x83, 0x89, 0xf8, + 0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf, + 0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e, + 0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x34, 0x30, + 0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, + 0xf2, 0x84, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, + 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, + 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, + 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, + 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, + 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, + 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, + 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31, + 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, + 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, + 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, + 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, + 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30, 0x38, 0x30, + 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35, 0x34, + 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37, + 0x31, 0x31, 0x32, 0x33, 0x30, 0x35, 0x34, 0x35, + 0x34, 0x30, 0x5a, 0x30, 0x81, 0x9d, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, + 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, + 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, + 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, + 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, + 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, + 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, + 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, + 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, + 0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17, + 0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e, + 0x5b, 0x54, 0xea, 0x8c, 0x6f, 0x79, 0xde, 0x94, + 0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b, 0x58, 0x12, + 0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86, + 0xbc, 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2, + 0x04, 0x03, 0x79, 0x9b, 0xc1, 0x5c, 0xf0, 0xf1, + 0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f, 0xdd, + 0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c, + 0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5, + 0x49, 0xf1, 0xae, 0x58, 0x9b, 0x94, 0x3a, 0x1f, + 0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9, 0xc0, 0xf3, + 0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c, + 0xf0, 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23, + 0x83, 0xe9, 0xe7, 0xcf, 0x9e, 0xa5, 0xf9, 0xcc, + 0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d, 0x63, + 0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1, + 0xe6, 0x9d, 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3, + 0xd6, 0x9a, 0x6d, 0xb9, 0x8e, 0x15, 0x51 }; + + public static InputStream asInputStream() { + byte[] data = new byte[DATA.length]; + for (int i = 0; i < data.length; i ++) { + data[i] = (byte) DATA[i]; + } + return new ByteArrayInputStream(data); + } + + public static char[] getCertificatePassword() { + return "secret".toCharArray(); + } + + public static char[] getKeyStorePassword() { + return "secret".toCharArray(); + } + + private SecureChatKeyStore() { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatPipelineFactory.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatPipelineFactory.java new file mode 100644 index 000000000000..1a967a4533b6 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatPipelineFactory.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import static net.gleamynode.netty.channel.Channels.*; + +import javax.net.ssl.SSLEngine; + +import net.gleamynode.netty.channel.ChannelHandler; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineFactory; +import net.gleamynode.netty.handler.codec.frame.DelimiterBasedFrameDecoder; +import net.gleamynode.netty.handler.codec.frame.Delimiters; +import net.gleamynode.netty.handler.codec.string.StringDecoder; +import net.gleamynode.netty.handler.codec.string.StringEncoder; +import net.gleamynode.netty.handler.ssl.SslHandler; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SecureChatPipelineFactory implements + ChannelPipelineFactory { + + private final ChannelHandler handler; + + public SecureChatPipelineFactory(ChannelHandler handler) { + this.handler = handler; + } + + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + + // Add SSL handler first to encrypt and decrypt everything. + // 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(); + engine.setUseClientMode(true); + } else { + engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); + engine.setUseClientMode(false); + } + + pipeline.addLast("ssl", new SslHandler(engine)); + + // On top of the SSL handler, add the text line codec. + pipeline.addLast("framer", new DelimiterBasedFrameDecoder( + 8192, Delimiters.lineDelimiter())); + pipeline.addLast("decoder", new StringDecoder()); + pipeline.addLast("encoder", new StringEncoder()); + + // and then business logic. + pipeline.addLast("handler", handler); + + return pipeline; + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServer.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServer.java new file mode 100644 index 000000000000..b4710452b96c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServer.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class SecureChatServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + SecureChatServerHandler handler = new SecureChatServerHandler(); + + bootstrap.setPipelineFactory(new SecureChatPipelineFactory(handler)); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServerHandler.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServerHandler.java new file mode 100644 index 000000000000..3dfbaabb3386 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatServerHandler.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.net.InetAddress; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; +import net.gleamynode.netty.handler.ssl.SslHandler; +import net.gleamynode.netty.util.MapBackedSet; + +@ChannelPipelineCoverage("all") +public class SecureChatServerHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + SecureChatServerHandler.class.getName()); + + static final Set channels = + new MapBackedSet(new ConcurrentHashMap()); + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + + // Get the SslHandler in the current pipeline. + final SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class); + + // Get notified when SSL handshake is done. + ChannelFuture handshakeFuture = sslHandler.handshake(e.getChannel()); + handshakeFuture.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) throws Exception { + if (future.isSuccess()) { + // Once session is secured, send a greeting. + future.getChannel().write( + "Welcome to " + InetAddress.getLocalHost().getHostName() + + " secure chat service!\n"); + future.getChannel().write( + "Your session is protected by " + + sslHandler.getEngine().getSession().getCipherSuite() + + " cipher suite.\n"); + + // Register the channel to the global channel list + // so the channel received the messages from others. + channels.add(future.getChannel()); + } else { + future.getChannel().close(); + } + } + }); + } + + @Override + public void channelDisconnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + // Unregister the channel from the global channel list + // so the channel doesn't receive messages anymore. + channels.remove(e.getChannel()); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + + // Convert to a String first. + String request = (String) e.getMessage(); + + // Send the received message to all channels but the current one. + for (Channel c: channels) { + if (c != e.getChannel()) { + c.write("[" + e.getChannel().getRemoteAddress() + "] " + + request + '\n'); + } + } + + // Close the connection if the client sent 'bye'. + if (request.toLowerCase().equals("bye")) { + e.getChannel().close(); + } + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatSslContextFactory.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatSslContextFactory.java new file mode 100644 index 000000000000..7dc4bff9662e --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatSslContextFactory.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.security.KeyStore; +import java.security.Security; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SecureChatSslContextFactory { + + private static final String PROTOCOL = "TLS"; + private static final SSLContext SERVER_CONTEXT; + private static final SSLContext CLIENT_CONTEXT; + + static { + String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); + if (algorithm == null) { + algorithm = "SunX509"; + } + + SSLContext serverContext = null; + SSLContext clientContext = null; + try { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(SecureChatKeyStore.asInputStream(), + SecureChatKeyStore.getKeyStorePassword()); + + // Set up key manager factory to use our key store + KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); + kmf.init(ks, SecureChatKeyStore.getCertificatePassword()); + + // Initialize the SSLContext to work with our key managers. + serverContext = SSLContext.getInstance(PROTOCOL); + serverContext.init( + kmf.getKeyManagers(), + SecureChatTrustManagerFactory.getTrustManagers(), null); + } catch (Exception e) { + throw new Error( + "Failed to initialize the server-side SSLContext", e); + } + + try { + clientContext = SSLContext.getInstance(PROTOCOL); + clientContext.init( + null, SecureChatTrustManagerFactory.getTrustManagers(), null); + } catch (Exception e) { + throw new Error( + "Failed to initialize the client-side SSLContext", e); + } + + SERVER_CONTEXT = serverContext; + CLIENT_CONTEXT = clientContext; + } + + public static SSLContext getServerContext() { + return SERVER_CONTEXT; + } + + public static SSLContext getClientContext() { + return CLIENT_CONTEXT; + } +} diff --git a/src/main/java/net/gleamynode/netty/example/securechat/SecureChatTrustManagerFactory.java b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatTrustManagerFactory.java new file mode 100644 index 000000000000..1461d0245edf --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/securechat/SecureChatTrustManagerFactory.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.securechat; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactorySpi; +import javax.net.ssl.X509TrustManager; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SecureChatTrustManagerFactory extends TrustManagerFactorySpi { + + private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + public void checkClientTrusted( + X509Certificate[] arg0, String arg1) throws CertificateException { + // Always trust - it's an example. + // You should do something in the real world. + } + + public void checkServerTrusted( + X509Certificate[] arg0, String arg1) throws CertificateException { + // Always trust - it's an example. + // You should do something in the real world. + } + }; + + public static TrustManager[] getTrustManagers() { + return new TrustManager[] { DUMMY_TRUST_MANAGER }; + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return getTrustManagers(); + } + + @Override + protected void engineInit(KeyStore keystore) throws KeyStoreException { + // Unused + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) + throws InvalidAlgorithmParameterException { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/example/telnet/TelnetClient.java b/src/main/java/net/gleamynode/netty/example/telnet/TelnetClient.java new file mode 100644 index 000000000000..a0cd2758b236 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/telnet/TelnetClient.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.telnet; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ClientBootstrap; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.socket.nio.NioClientSocketChannelFactory; + +public class TelnetClient { + + public static void main(String[] args) throws Exception { + // Print usage if no argument is specified. + if (args.length != 2) { + System.err.println( + "Usage: " + TelnetClient.class.getSimpleName() + + " "); + return; + } + + // Parse options. + String host = args[0]; + int port = Integer.parseInt(args[1]); + + // Start client. + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ClientBootstrap bootstrap = new ClientBootstrap(factory); + TelnetClientHandler handler = new TelnetClientHandler(); + + bootstrap.setPipelineFactory(new TelnetPipelineFactory(handler)); + bootstrap.setOption("tcpNoDelay", true); + bootstrap.setOption("keepAlive", true); + + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); + + // Wait until the connection attempt succeeds or fails. + Channel channel = future.awaitUninterruptibly().getChannel(); + if (!future.isSuccess()) { + future.getCause().printStackTrace(); + System.exit(0); + } + + // Read commands from the stdin. + ChannelFuture lastWriteFuture = null; + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + for (;;) { + String line = in.readLine(); + if (line == null) { + break; + } + lastWriteFuture = channel.write(line + '\n'); + } + + // Wait until all messages are flushed before closing the channel. + if (lastWriteFuture != null) { + lastWriteFuture.awaitUninterruptibly(); + } + + channel.close().awaitUninterruptibly(); + + // We should shut down all thread pools here to exit normally. + // However, it's just fine to call System.exit(0) because we are + // finished with the business. + System.exit(0); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/telnet/TelnetClientHandler.java b/src/main/java/net/gleamynode/netty/example/telnet/TelnetClientHandler.java new file mode 100644 index 000000000000..57aba0971bfd --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/telnet/TelnetClientHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.telnet; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("all") +public class TelnetClientHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + TelnetClientHandler.class.getName()); + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + System.err.println(e.getMessage()); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/telnet/TelnetPipelineFactory.java b/src/main/java/net/gleamynode/netty/example/telnet/TelnetPipelineFactory.java new file mode 100644 index 000000000000..681b9522c5f3 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/telnet/TelnetPipelineFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.telnet; + +import static net.gleamynode.netty.channel.Channels.*; +import net.gleamynode.netty.channel.ChannelHandler; +import net.gleamynode.netty.channel.ChannelPipeline; +import net.gleamynode.netty.channel.ChannelPipelineFactory; +import net.gleamynode.netty.handler.codec.frame.DelimiterBasedFrameDecoder; +import net.gleamynode.netty.handler.codec.frame.Delimiters; +import net.gleamynode.netty.handler.codec.string.StringDecoder; +import net.gleamynode.netty.handler.codec.string.StringEncoder; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class TelnetPipelineFactory implements + ChannelPipelineFactory { + + private final ChannelHandler handler; + + public TelnetPipelineFactory(ChannelHandler handler) { + this.handler = handler; + } + + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = pipeline(); + + // Add the text line codec first, + pipeline.addLast("framer", new DelimiterBasedFrameDecoder( + 8192, Delimiters.lineDelimiter())); + pipeline.addLast("decoder", new StringDecoder()); + pipeline.addLast("encoder", new StringEncoder()); + + // and then business logic. + pipeline.addLast("handler", handler); + + return pipeline; + } +} diff --git a/src/main/java/net/gleamynode/netty/example/telnet/TelnetServer.java b/src/main/java/net/gleamynode/netty/example/telnet/TelnetServer.java new file mode 100644 index 000000000000..77b634351201 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/telnet/TelnetServer.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.telnet; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executors; + +import net.gleamynode.netty.bootstrap.ServerBootstrap; +import net.gleamynode.netty.channel.ChannelFactory; +import net.gleamynode.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class TelnetServer { + + public static void main(String[] args) throws Exception { + // Start server. + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + TelnetServerHandler handler = new TelnetServerHandler(); + + bootstrap.setPipelineFactory(new TelnetPipelineFactory(handler)); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.keepAlive", true); + + bootstrap.bind(new InetSocketAddress(8080)); + } +} diff --git a/src/main/java/net/gleamynode/netty/example/telnet/TelnetServerHandler.java b/src/main/java/net/gleamynode/netty/example/telnet/TelnetServerHandler.java new file mode 100644 index 000000000000..1f966738a8c1 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/example/telnet/TelnetServerHandler.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.example.telnet; + +import java.net.InetAddress; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +@ChannelPipelineCoverage("all") +public class TelnetServerHandler extends SimpleChannelHandler { + + private static final Logger logger = Logger.getLogger( + TelnetServerHandler.class.getName()); + + @Override + public void handleUpstream( + ChannelHandlerContext ctx, ChannelEvent e) throws Exception { + if (e instanceof ChannelStateEvent) { + logger.info(e.toString()); + } + super.handleUpstream(ctx, e); + } + + @Override + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + // Send greeting. + e.getChannel().write( + "Welcome to " + InetAddress.getLocalHost().getHostName() + "!\n"); + e.getChannel().write("It's " + new Date() + " now.\n"); + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) { + + // Convert to a String first. + String request = (String) e.getMessage(); + + // Generate and write a response. + String response; + boolean close = false; + if (request.length() == 0) { + response = "Please type something.\n"; + } else if (request.toLowerCase().equals("bye")) { + response = "Have a good day!\n"; + close = true; + } else { + response = "Did you say '" + request + "'?\n"; + } + + ChannelFuture future = e.getChannel().write(response); + + // Close the connection after sending 'Have a good day!' + if (close) { + future.addListener(ChannelFutureListener.CLOSE); + } + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) { + logger.log( + Level.WARNING, + "Unexpected exception from downstream.", + e.getCause()); + e.getChannel().close(); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/DelimiterBasedFrameDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/DelimiterBasedFrameDecoder.java new file mode 100644 index 000000000000..11b0280218ad --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/DelimiterBasedFrameDecoder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.frame; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + * @apiviz.uses net.gleamynode.netty.handler.codec.frame.Delimiters - - optional yet useful + */ +public class DelimiterBasedFrameDecoder extends FrameDecoder { + + private final ChannelBuffer[] delimiters; + private final int maxFrameLength; + + public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer delimiter) { + validateMaxFrameLength(maxFrameLength); + validateDelimiter(delimiter); + delimiters = new ChannelBuffer[] { + delimiter.slice( + delimiter.readerIndex(), delimiter.readableBytes()) + }; + this.maxFrameLength = maxFrameLength; + } + + public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer... delimiters) { + validateMaxFrameLength(maxFrameLength); + if (delimiters == null) { + throw new NullPointerException("delimiters"); + } + if (delimiters.length == 0) { + throw new IllegalArgumentException("empty delimiters"); + } + this.delimiters = new ChannelBuffer[delimiters.length]; + for (int i = 0; i < delimiters.length; i ++) { + ChannelBuffer d = delimiters[i]; + validateDelimiter(d); + this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes()); + } + this.maxFrameLength = maxFrameLength; + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + // Try all delimiters. + for (ChannelBuffer delim: delimiters) { + int delimIndex = indexOf(buffer, delim); + if (delimIndex > 0) { + ChannelBuffer frame = buffer.readBytes(delimIndex); + if (frame.capacity() > maxFrameLength) { + fail(); + } + buffer.skipBytes(delim.capacity()); + return frame; + } else if (delimIndex == 0) { + buffer.skipBytes(delim.capacity()); + return ChannelBuffer.EMPTY_BUFFER; + } + } + + if (buffer.readableBytes() > maxFrameLength) { + fail(); + } + return null; + } + + private void fail() throws TooLongFrameException { + throw new TooLongFrameException( + "The frame length exceeds " + maxFrameLength); + } + + private static int indexOf(ChannelBuffer haystack, ChannelBuffer needle) { + for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) { + int haystackIndex = i; + int needleIndex; + for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) { + if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) { + break; + } else { + haystackIndex ++; + if (haystackIndex == haystack.writerIndex() && + needleIndex != needle.capacity() - 1) { + return -1; + } + } + } + + if (needleIndex == needle.capacity()) { + // Found the needle from the haystack! + return i - haystack.readerIndex(); + } + } + return -1; + } + + private static void validateDelimiter(ChannelBuffer delimiter) { + if (delimiter == null) { + throw new NullPointerException("delimiter"); + } + if (!delimiter.readable()) { + throw new IllegalArgumentException("empty delimiter"); + } + } + + private static void validateMaxFrameLength(int maxFrameLength) { + if (maxFrameLength <= 0) { + throw new IllegalArgumentException( + "maxFrameLength must be a positive integer: " + + maxFrameLength); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/Delimiters.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/Delimiters.java new file mode 100644 index 000000000000..0f5c75ca2adf --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/Delimiters.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.frame; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +public class Delimiters { + + public static ChannelBuffer[] nulDelimiter() { + return new ChannelBuffer[] { + ChannelBuffers.wrappedBuffer(new byte[] { 0 }) }; + } + + public static ChannelBuffer[] lineDelimiter() { + return new ChannelBuffer[] { + ChannelBuffers.wrappedBuffer(new byte[] { '\r', '\n' }), + ChannelBuffers.wrappedBuffer(new byte[] { '\n' }), + ChannelBuffers.wrappedBuffer(new byte[] { '\r' }) }; + } + + private Delimiters() { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/FixedLengthFrameDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/FixedLengthFrameDecoder.java new file mode 100644 index 000000000000..c6ad480ab3f7 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/FixedLengthFrameDecoder.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.frame; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +public class FixedLengthFrameDecoder extends FrameDecoder { + + private final int frameLength; + + public FixedLengthFrameDecoder(int frameLength) { + if (frameLength <= 0) { + throw new IllegalArgumentException( + "frameLength must be a positive integer: " + frameLength); + } + this.frameLength = frameLength; + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + if (buffer.readableBytes() < frameLength) { + return null; + } else { + return buffer.readBytes(frameLength); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/FrameDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/FrameDecoder.java new file mode 100644 index 000000000000..aa45d02a5f6a --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/FrameDecoder.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.frame; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.net.SocketAddress; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +@ChannelPipelineCoverage("one") +public abstract class FrameDecoder extends SimpleChannelHandler { + + private final ChannelBuffer cumulation = ChannelBuffers.dynamicBuffer(); + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception { + + Object m = e.getMessage(); + if (!(m instanceof ChannelBuffer)) { + ctx.sendUpstream(e); + return; + } + + ChannelBuffer input = (ChannelBuffer) m; + if (!input.readable()) { + return; + } + + if (cumulation.readable()) { + cumulation.discardReadBytes(); + cumulation.writeBytes(input); + callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress()); + } else { + callDecode(ctx, e.getChannel(), input, e.getRemoteAddress()); + if (input.readable()) { + cumulation.writeBytes(input); + } + } + } + + @Override + public void channelDisconnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + cleanup(ctx, e); + } + + @Override + public void channelClosed( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + cleanup(ctx, e); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + ctx.sendUpstream(e); + } + + protected abstract Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception; + + protected Object decodeLast( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + return decode(ctx, channel, buffer); + } + + private void callDecode( + ChannelHandlerContext context, Channel channel, + ChannelBuffer cumulation, SocketAddress remoteAddress) throws Exception { + + while (cumulation.readable()) { + int oldReaderIndex = cumulation.readerIndex(); + Object frame = decode(context, channel, cumulation); + if (frame == null) { + if (oldReaderIndex == cumulation.readerIndex()) { + // Seems like more data is required. + // Let's wait for the next notification. + break; + } else { + // Previous data has been discarded. + // Probably it's reading on. + continue; + } + } else if (oldReaderIndex == cumulation.readerIndex()) { + throw new IllegalStateException( + "decode() method must read at least one byte " + + "if it returned a frame."); + } + + fireMessageReceived(context, channel, frame, remoteAddress); + } + } + + private void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + try { + if (cumulation.readable()) { + // Make sure all frames are read before notifying a closed channel. + callDecode(ctx, e.getChannel(), cumulation, null); + if (cumulation.readable()) { + // and send the remainders too if necessary. + Object partialFrame = decodeLast(ctx, e.getChannel(), cumulation); + if (partialFrame != null) { + fireMessageReceived(ctx, e.getChannel(), partialFrame, null); + } + } + } + } finally { + ctx.sendUpstream(e); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/TooLongFrameException.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/TooLongFrameException.java new file mode 100644 index 000000000000..82299e957c1d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/TooLongFrameException.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.frame; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + * @apiviz.hidden + */ +public class TooLongFrameException extends Exception { + + private static final long serialVersionUID = -1995801950698951640L; + + public TooLongFrameException() { + super(); + } + + public TooLongFrameException(String message, Throwable cause) { + super(message, cause); + } + + public TooLongFrameException(String message) { + super(message); + } + + public TooLongFrameException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/frame/package-info.java b/src/main/java/net/gleamynode/netty/handler/codec/frame/package-info.java new file mode 100644 index 000000000000..dc8795811716 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/frame/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Extensible decoder and its common implementations which deal with the + * packet fragmentation and reassembly issue found in a stream-based transport + * such as TCP/IP. + */ +package net.gleamynode.netty.handler.codec.frame; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayError.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayError.java new file mode 100644 index 000000000000..4f38007de8f5 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayError.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.replay; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +class ReplayError extends Error { + + private static final long serialVersionUID = 2666698631187527681L; + + ReplayError() { + super(); + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoder.java new file mode 100644 index 000000000000..f0ddab282da2 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoder.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.replay; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.net.SocketAddress; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.Channels; +import net.gleamynode.netty.channel.ExceptionEvent; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.channel.SimpleChannelHandler; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +@ChannelPipelineCoverage("one") +public abstract class ReplayingDecoder> extends SimpleChannelHandler { + + private final ChannelBuffer cumulation = new UnsafeDynamicChannelBuffer(256); + private final ReplayingDecoderBuffer replayable = new ReplayingDecoderBuffer(cumulation); + private volatile T state; + private volatile int checkpoint; + + protected ReplayingDecoder() { + this(null); + } + + protected ReplayingDecoder(T initialState) { + this.state = initialState; + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) + throws Exception { + + Object m = e.getMessage(); + if (!(m instanceof ChannelBuffer)) { + ctx.sendUpstream(e); + return; + } + + ChannelBuffer input = (ChannelBuffer) m; + if (!input.readable()) { + return; + } + + cumulation.discardReadBytes(); + cumulation.writeBytes(input); + callDecode(ctx, e.getChannel(), e.getRemoteAddress()); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + cleanup(ctx, e); + } + + @Override + public void channelClosed(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + cleanup(ctx, e); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + throws Exception { + ctx.sendUpstream(e); + } + + private void callDecode(ChannelHandlerContext context, Channel channel, SocketAddress remoteAddress) throws Exception { + while (cumulation.readable()) { + int oldReaderIndex = checkpoint = cumulation.readerIndex(); + Object result = null; + try { + result = decode(context, channel, replayable, state); + if (result == null) { + if (oldReaderIndex == cumulation.readerIndex()) { + throw new IllegalStateException( + "null cannot be returned if no data is consumed."); + } else { + // Previous data has been discarded. + // Probably it's reading on. + continue; + } + } + } catch (ReplayError replay) { + // Return to the checkpoint (or oldPosition) and retry. + cumulation.readerIndex(checkpoint); + } + + if (result == null) { + // Seems like more data is required. + // Let's wait for the next notification. + break; + } + + if (oldReaderIndex == cumulation.readerIndex()) { + throw new IllegalStateException( + "decode() method must consume at least one byte " + + "if it returned a decoded message."); + } + + // A successful decode + Channels.fireMessageReceived(context, channel, result, remoteAddress); + } + } + + private void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) + throws Exception { + try { + if (cumulation.readable()) { + // Make sure all data was read before notifying a closed channel. + callDecode(ctx, e.getChannel(), null); + if (cumulation.readable()) { + // and send the remainders too if necessary. + Object partiallyDecoded = decodeLast(ctx, e.getChannel(), cumulation, state); + if (partiallyDecoded != null) { + fireMessageReceived(ctx, e.getChannel(), partiallyDecoded, null); + } + } + } + } catch (ReplayError replay) { + // Ignore + } finally { + ctx.sendUpstream(e); + } + } + + protected void checkpoint() { + checkpoint = cumulation.readerIndex(); + } + + protected void checkpoint(T state) { + this.state = state; + checkpoint = cumulation.readerIndex(); + } + + protected abstract Object decode(ChannelHandlerContext ctx, + Channel channel, ChannelBuffer buffer, T state) throws Exception; + + protected Object decodeLast( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, T state) throws Exception { + return decode(ctx, channel, buffer, state); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoderBuffer.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoderBuffer.java new file mode 100644 index 000000000000..828297344804 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/ReplayingDecoderBuffer.java @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.replay; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferIndexFinder; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +class ReplayingDecoderBuffer implements ChannelBuffer { + + private static final Error REPLAY = new ReplayError(); + + private final ChannelBuffer buffer; + + ReplayingDecoderBuffer(ChannelBuffer buffer) { + this.buffer = buffer; + } + + public int capacity() { + return Integer.MAX_VALUE; + } + + public void clear() { + reject(); + } + + public int compareTo(ChannelBuffer buffer) { + reject(); + return 0; + } + + public ChannelBuffer copy() { + reject(); + return null; + } + + public ChannelBuffer copy(int index, int length) { + checkIndex(index, length); + return buffer.copy(index, length); + } + + public void discardReadBytes() { + reject(); + } + + public ChannelBuffer duplicate() { + reject(); + return null; + } + + @Override + public boolean equals(Object obj) { + reject(); + return false; + } + + public byte getByte(int index) { + checkIndex(index); + return buffer.getByte(index); + } + + public void getBytes(int index, byte[] dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, byte[] dst) { + checkIndex(index, dst.length); + buffer.getBytes(index, dst); + } + + public void getBytes(int index, ByteBuffer dst) { + checkIndex(index, dst.remaining()); + buffer.getBytes(index, dst); + } + + public void getBytes(int index, ChannelBuffer dst, int dstIndex, int length) { + checkIndex(index, length); + buffer.getBytes(index, dst, dstIndex, length); + } + + public void getBytes(int index, ChannelBuffer dst) { + checkIndex(index, dst.writableBytes()); + buffer.getBytes(index, dst); + } + + public int getBytes(int index, GatheringByteChannel out, int length) + throws IOException { + reject(); + return -1; + } + + public void getBytes(int index, OutputStream out, int length) + throws IOException { + reject(); + } + + public int getInt(int index) { + checkIndex(index); + return buffer.getInt(index); + } + + public long getLong(int index) { + checkIndex(index, 8); + return buffer.getLong(index); + } + + public int getMedium(int index) { + checkIndex(index, 3); + return buffer.getMedium(index); + } + + public short getShort(int index) { + checkIndex(index, 2); + return buffer.getShort(index); + } + + @Override + public int hashCode() { + reject(); + return 0; + } + + public int indexOf(int fromIndex, int toIndex, byte value) { + int endIndex = indexOf(buffer.readerIndex(), buffer.writerIndex(), value); + if (endIndex < 0) { + throw REPLAY; + } + return endIndex; + } + + public int indexOf(int fromIndex, int toIndex, + ChannelBufferIndexFinder indexFinder) { + int endIndex = indexOf(buffer.readerIndex(), buffer.writerIndex(), indexFinder); + if (endIndex < 0) { + throw REPLAY; + } + return endIndex; + } + + public void markReaderIndex() { + buffer.markReaderIndex(); + } + + public void markWriterIndex() { + reject(); + } + + public ByteOrder order() { + return buffer.order(); + } + + public boolean readable() { + return true; + } + + public int readableBytes() { + return Integer.MAX_VALUE - buffer.readerIndex(); + } + + public byte readByte() { + checkReadableBytes(1); + return buffer.readByte(); + } + + public ChannelBuffer readBytes() { + reject(); + return null; + } + + public void readBytes(byte[] dst, int dstIndex, int length) { + checkReadableBytes(length); + buffer.readBytes(dst, dstIndex, length); + } + + public void readBytes(byte[] dst) { + checkReadableBytes(dst.length); + buffer.readBytes(dst); + } + + public void readBytes(ByteBuffer dst) { + checkReadableBytes(dst.remaining()); + buffer.readBytes(dst); + } + + public void readBytes(ChannelBuffer dst, int dstIndex, int length) { + checkReadableBytes(length); + buffer.readBytes(dst, dstIndex, length); + } + + public void readBytes(ChannelBuffer dst, int length) { + checkReadableBytes(length); + buffer.readBytes(dst, length); + } + + public void readBytes(ChannelBuffer dst) { + checkReadableBytes(dst.writableBytes()); + buffer.readBytes(dst); + } + + public ChannelBuffer readBytes(ChannelBufferIndexFinder endIndexFinder) { + int endIndex = buffer.indexOf(buffer.readerIndex(), buffer.writerIndex(), endIndexFinder); + if (endIndex < 0) { + throw REPLAY; + } + return readBytes(endIndex); + } + + public int readBytes(GatheringByteChannel out, int length) + throws IOException { + reject(); + return -1; + } + + public ChannelBuffer readBytes(int length) { + checkReadableBytes(length); + return buffer.readBytes(length); + } + + public void readBytes(OutputStream out, int length) throws IOException { + reject(); + } + + public int readerIndex() { + return buffer.readerIndex(); + } + + public void readerIndex(int readerIndex) { + buffer.readerIndex(readerIndex); + } + + public int readInt() { + checkReadableBytes(4); + return buffer.readInt(); + } + + public long readLong() { + checkReadableBytes(8); + return buffer.readLong(); + } + + public int readMedium() { + checkReadableBytes(3); + return buffer.readMedium(); + } + + public short readShort() { + checkReadableBytes(2); + return buffer.readShort(); + } + + public void resetReaderIndex() { + buffer.resetReaderIndex(); + } + + public void resetWriterIndex() { + reject(); + } + + public void setByte(int index, byte value) { + reject(); + } + + public void setBytes(int index, byte[] src, int srcIndex, int length) { + reject(); + } + + public void setBytes(int index, byte[] src) { + reject(); + } + + public void setBytes(int index, ByteBuffer src) { + reject(); + } + + public void setBytes(int index, ChannelBuffer src, int srcIndex, int length) { + reject(); + } + + public void setBytes(int index, ChannelBuffer src) { + reject(); + } + + public void setBytes(int index, InputStream in, int length) + throws IOException { + reject(); + } + + public int setBytes(int index, ScatteringByteChannel in, int length) + throws IOException { + reject(); + return -1; + } + + public void setIndex(int readerIndex, int writerIndex) { + reject(); + } + + public void setInt(int index, int value) { + reject(); + } + + public void setLong(int index, long value) { + reject(); + } + + public void setMedium(int index, int value) { + reject(); + } + + public void setShort(int index, short value) { + reject(); + } + + public int skipBytes(ChannelBufferIndexFinder firstIndexFinder) { + int oldReaderIndex = buffer.readerIndex(); + int newReaderIndex = buffer.indexOf(oldReaderIndex, buffer.writerIndex(), firstIndexFinder); + if (newReaderIndex < 0) { + throw REPLAY; + } + buffer.readerIndex(newReaderIndex); + return newReaderIndex - oldReaderIndex; + } + + public void skipBytes(int length) { + checkReadableBytes(length); + buffer.skipBytes(length); + } + + public ChannelBuffer slice() { + reject(); + return null; + } + + public ChannelBuffer slice(int index, int length) { + checkIndex(index, length); + return buffer.slice(index, length); + } + + public ByteBuffer toByteBuffer() { + reject(); + return null; + } + + public ByteBuffer toByteBuffer(int index, int length) { + return buffer.toByteBuffer(index, length); + } + + public ByteBuffer[] toByteBuffers() { + reject(); + return null; + } + + public ByteBuffer[] toByteBuffers(int index, int length) { + checkIndex(index, length); + return buffer.toByteBuffers(index, length); + } + + @Override + public String toString() { + return buffer.toString(); + } + + public boolean writable() { + return false; + } + + public int writableBytes() { + return 0; + } + + public void writeByte(byte value) { + reject(); + } + + public void writeBytes(byte[] src, int srcIndex, int length) { + reject(); + } + + public void writeBytes(byte[] src) { + reject(); + } + + public void writeBytes(ByteBuffer src) { + reject(); + } + + public void writeBytes(ChannelBuffer src, int srcIndex, int length) { + reject(); + } + + public void writeBytes(ChannelBuffer src, int length) { + reject(); + } + + public void writeBytes(ChannelBuffer src) { + reject(); + } + + public void writeBytes(InputStream in, int length) throws IOException { + reject(); + } + + public int writeBytes(ScatteringByteChannel in, int length) + throws IOException { + reject(); + return -1; + } + + public void writeInt(int value) { + reject(); + } + + public void writeLong(long value) { + reject(); + } + + public void writeMedium(int value) { + reject(); + } + + public void writePlaceholder(int length) { + reject(); + } + + public int writerIndex() { + return buffer.writerIndex(); + } + + public void writerIndex(int writerIndex) { + reject(); + } + + public void writeShort(short value) { + reject(); + } + + private void checkIndex(int index) { + if (index > buffer.writerIndex()) { + throw REPLAY; + } + } + + private void checkIndex(int index, int length) { + if (index + length > buffer.writerIndex()) { + throw REPLAY; + } + } + + private void checkReadableBytes(int readableBytes) { + if (buffer.readableBytes() < readableBytes) { + throw REPLAY; + } + } + + private void reject() { + throw new UnsupportedOperationException( + "Unsupported in " + ReplayingDecoder.class.getSimpleName()); + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java new file mode 100644 index 000000000000..a5d2e25b73d7 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.replay; + +import java.nio.ByteOrder; + +import net.gleamynode.netty.buffer.DynamicChannelBuffer; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +class UnsafeDynamicChannelBuffer extends DynamicChannelBuffer { + + UnsafeDynamicChannelBuffer(int estimatedLength) { + super(estimatedLength); + } + + UnsafeDynamicChannelBuffer(ByteOrder endianness, int estimatedLength) { + super(endianness, estimatedLength); + } + + @Override + protected void checkReadableBytes(int minReaderRemaining) { + // Don't check here - ReplayingDecoderBuffer will check. + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/VoidEnum.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/VoidEnum.java new file mode 100644 index 000000000000..8f29c9655e30 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/VoidEnum.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.replay; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public enum VoidEnum { + // No state is defined. +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/replay/package-info.java b/src/main/java/net/gleamynode/netty/handler/codec/replay/package-info.java new file mode 100644 index 000000000000..7366f921bc4b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/replay/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Specialized decoder which enables implementation of a non-blocking decoder + * with blocking I/O paradigm. + */ +package net.gleamynode.netty.handler.codec.replay; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectInputStream.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectInputStream.java new file mode 100644 index 000000000000..05bcf93b3005 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectInputStream.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; +import java.io.StreamCorruptedException; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +class CompactObjectInputStream extends ObjectInputStream { + + private final ClassLoader classLoader; + + CompactObjectInputStream(InputStream in) throws IOException { + this(in, Thread.currentThread().getContextClassLoader()); + } + + CompactObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { + super(in); + this.classLoader = classLoader; + } + + @Override + protected void readStreamHeader() throws IOException, + StreamCorruptedException { + int version = readByte() & 0xFF; + if (version != STREAM_VERSION) { + throw new StreamCorruptedException( + "Unsupported version: " + version); + } + } + + @Override + protected ObjectStreamClass readClassDescriptor() + throws IOException, ClassNotFoundException { + int type = read(); + if (type < 0) { + throw new EOFException(); + } + switch (type) { + case CompactObjectOutputStream.TYPE_PRIMITIVE: + return super.readClassDescriptor(); + case CompactObjectOutputStream.TYPE_NON_PRIMITIVE: + String className = readUTF(); + Class clazz = + Class.forName(className, true, classLoader); + return ObjectStreamClass.lookup(clazz); + default: + throw new StreamCorruptedException( + "Unexpected class descriptor type: " + type); + } + } + + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + try { + return Class.forName(name, false, classLoader); + } catch (ClassNotFoundException ex) { + return super.resolveClass(desc); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectOutputStream.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectOutputStream.java new file mode 100644 index 000000000000..9bf2952d98ea --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompactObjectOutputStream.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +class CompactObjectOutputStream extends ObjectOutputStream { + + static final int TYPE_PRIMITIVE = 0; + static final int TYPE_NON_PRIMITIVE = 1; + + CompactObjectOutputStream(OutputStream out) throws IOException { + super(out); + } + + @Override + protected void writeStreamHeader() throws IOException { + writeByte(STREAM_VERSION); + } + + @Override + protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { + if (desc.forClass().isPrimitive()) { + write(TYPE_PRIMITIVE); + super.writeClassDescriptor(desc); + } else { + write(TYPE_NON_PRIMITIVE); + writeUTF(desc.getName()); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoder.java new file mode 100644 index 000000000000..91914ac14c79 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoder.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamConstants; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferInputStream; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.handler.codec.replay.ReplayingDecoder; +import net.gleamynode.netty.util.SwitchableInputStream; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class CompatibleObjectDecoder extends ReplayingDecoder { + + private final SwitchableInputStream bin = new SwitchableInputStream(); + private volatile ObjectInputStream oin; + + public CompatibleObjectDecoder() { + super(CompatibleObjectDecoderState.READ_HEADER); + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, CompatibleObjectDecoderState state) throws Exception { + bin.switchStream(new ChannelBufferInputStream(buffer)); + + switch (state) { + case READ_HEADER: + oin = newObjectInputStream(bin); + checkpoint(CompatibleObjectDecoderState.READ_OBJECT); + case READ_OBJECT: + return oin.readObject(); + default: + throw new IllegalStateException("Unknown state: " + state); + } + } + + protected ObjectInputStream newObjectInputStream(InputStream in) throws Exception { + return new ObjectInputStream(in); + } + + @Override + protected Object decodeLast(ChannelHandlerContext ctx, Channel channel, + ChannelBuffer buffer, CompatibleObjectDecoderState state) + throws Exception { + // Ignore the last TC_RESET + if (buffer.readableBytes() == 1 && + buffer.getByte(buffer.readerIndex()) == ObjectStreamConstants.TC_RESET) { + buffer.skipBytes(1); + oin.close(); + return null; + } + + Object decoded = decode(ctx, channel, buffer, state); + oin.close(); + return decoded; + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoderState.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoderState.java new file mode 100644 index 000000000000..e851296bd21c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectDecoderState.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +enum CompatibleObjectDecoderState { + READ_HEADER, + READ_OBJECT, +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectEncoder.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectEncoder.java new file mode 100644 index 000000000000..68b490a9295f --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/CompatibleObjectEncoder.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import static net.gleamynode.netty.buffer.ChannelBuffers.*; +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferOutputStream; +import net.gleamynode.netty.channel.ChannelDownstreamHandler; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +@ChannelPipelineCoverage("all") +public class CompatibleObjectEncoder implements ChannelDownstreamHandler { + + private final ChannelBuffer buffer = dynamicBuffer(); + private final int resetInterval; + private volatile ObjectOutputStream oout; + private int writtenObjects; + + public CompatibleObjectEncoder() { + this(16); // Reset at every sixteen writes + } + + public CompatibleObjectEncoder(int resetInterval) { + if (resetInterval < 0) { + throw new IllegalArgumentException( + "resetInterval: " + resetInterval); + } + this.resetInterval = resetInterval; + } + + public void handleDownstream( + ChannelHandlerContext context, ChannelEvent evt) throws Exception { + if (!(evt instanceof MessageEvent)) { + context.sendDownstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + + buffer.clear(); + if (oout == null) { + oout = newObjectOutputStream(new ChannelBufferOutputStream(buffer)); + } + + if (resetInterval != 0) { + // Resetting will prevent OOM on the receiving side. + writtenObjects ++; + if (writtenObjects % resetInterval == 0) { + oout.reset(); + } + } + oout.writeObject(e.getMessage()); + oout.flush(); + + ChannelBuffer encoded = buffer.readBytes(buffer.readableBytes()); + write(context, e.getChannel(), e.getFuture(), encoded, e.getRemoteAddress()); + } + + protected ObjectOutputStream newObjectOutputStream(OutputStream out) throws Exception { + return new ObjectOutputStream(out); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoder.java new file mode 100644 index 000000000000..df4572e56e84 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoder.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.StreamCorruptedException; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferInputStream; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.handler.codec.frame.FrameDecoder; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ObjectDecoder extends FrameDecoder { + + private final int maxObjectSize; + private final ClassLoader classLoader; + + public ObjectDecoder() { + this(1048576); + } + + public ObjectDecoder(int maxObjectSize) { + this(maxObjectSize, Thread.currentThread().getContextClassLoader()); + } + + public ObjectDecoder(int maxObjectSize, ClassLoader classLoader) { + if (maxObjectSize <= 0) { + throw new IllegalArgumentException("maxObjectSize: " + maxObjectSize); + } + if (classLoader == null) { + classLoader = Thread.currentThread().getContextClassLoader(); + } + + this.maxObjectSize = maxObjectSize; + this.classLoader = classLoader; + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + if (buffer.readableBytes() < 4) { + return null; + } + + int dataLen = buffer.getInt(buffer.readerIndex()); + if (dataLen <= 0) { + throw new StreamCorruptedException("invalid data length: " + dataLen); + } + if (dataLen > maxObjectSize) { + throw new StreamCorruptedException( + "data length too big: " + dataLen + " (max: " + maxObjectSize + ')'); + } + + if (buffer.readableBytes() < dataLen + 4) { + return null; + } + + buffer.skipBytes(4); + return new CompactObjectInputStream( + new ChannelBufferInputStream(buffer, dataLen), + classLoader).readObject(); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoderInputStream.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoderInputStream.java new file mode 100644 index 000000000000..fb5f3c3afeb6 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectDecoderInputStream.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.StreamCorruptedException; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ObjectDecoderInputStream extends InputStream implements + ObjectInput { + + private final DataInputStream in; + private final ClassLoader classLoader; + private final int maxObjectSize; + + public ObjectDecoderInputStream(InputStream in) { + this(in, Thread.currentThread().getContextClassLoader()); + } + + public ObjectDecoderInputStream(InputStream in, ClassLoader classLoader) { + this(in, classLoader, 1048576); + } + + public ObjectDecoderInputStream(InputStream in, int maxObjectSize) { + this(in, Thread.currentThread().getContextClassLoader(), maxObjectSize); + } + + public ObjectDecoderInputStream(InputStream in, ClassLoader classLoader, int maxObjectSize) { + if (in == null) { + throw new NullPointerException("in"); + } + if (maxObjectSize <= 0) { + throw new IllegalArgumentException("maxObjectSize: " + maxObjectSize); + } + if (in instanceof DataInputStream) { + this.in = (DataInputStream) in; + } else { + this.in = new DataInputStream(in); + } + this.classLoader = classLoader; + this.maxObjectSize = maxObjectSize; + } + + public Object readObject() throws ClassNotFoundException, IOException { + int dataLen = readInt(); + if (dataLen <= 0) { + throw new StreamCorruptedException("invalid data length: " + dataLen); + } + if (dataLen > maxObjectSize) { + throw new StreamCorruptedException( + "data length too big: " + dataLen + " (max: " + maxObjectSize + ')'); + } + + byte[] data = new byte[dataLen]; + readFully(data); + + return new CompactObjectInputStream(in, classLoader).readObject(); + } + + @Override + public int available() throws IOException { + return in.available(); + } + + @Override + public void close() throws IOException { + in.close(); + } + + @Override + public void mark(int readlimit) { + in.mark(readlimit); + } + + @Override + public boolean markSupported() { + return in.markSupported(); + } + + @Override + public int read() throws IOException { + return in.read(); + } + + @Override + public final int read(byte[] b, int off, int len) throws IOException { + return in.read(b, off, len); + } + + @Override + public final int read(byte[] b) throws IOException { + return in.read(b); + } + + public final boolean readBoolean() throws IOException { + return in.readBoolean(); + } + + public final byte readByte() throws IOException { + return in.readByte(); + } + + public final char readChar() throws IOException { + return in.readChar(); + } + + public final double readDouble() throws IOException { + return in.readDouble(); + } + + public final float readFloat() throws IOException { + return in.readFloat(); + } + + public final void readFully(byte[] b, int off, int len) throws IOException { + in.readFully(b, off, len); + } + + public final void readFully(byte[] b) throws IOException { + in.readFully(b); + } + + public final int readInt() throws IOException { + return in.readInt(); + } + + @Deprecated + public final String readLine() throws IOException { + return in.readLine(); + } + + public final long readLong() throws IOException { + return in.readLong(); + } + + public final short readShort() throws IOException { + return in.readShort(); + } + + public final int readUnsignedByte() throws IOException { + return in.readUnsignedByte(); + } + + public final int readUnsignedShort() throws IOException { + return in.readUnsignedShort(); + } + + public final String readUTF() throws IOException { + return in.readUTF(); + } + + @Override + public void reset() throws IOException { + in.reset(); + } + + @Override + public long skip(long n) throws IOException { + return in.skip(n); + } + + public final int skipBytes(int n) throws IOException { + return in.skipBytes(n); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoder.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoder.java new file mode 100644 index 000000000000..764fb00e1deb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoder.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import static net.gleamynode.netty.buffer.ChannelBuffers.*; +import static net.gleamynode.netty.channel.Channels.*; + +import java.io.ObjectOutputStream; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferOutputStream; +import net.gleamynode.netty.channel.ChannelDownstreamHandler; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +@ChannelPipelineCoverage("all") +public class ObjectEncoder implements ChannelDownstreamHandler { + private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; + + private final int estimatedLength; + + public ObjectEncoder() { + this(512); + } + + public ObjectEncoder(int estimatedLength) { + if (estimatedLength <= 0) { + throw new IllegalArgumentException( + "estimatedLength: " + estimatedLength); + } + this.estimatedLength = estimatedLength; + } + + public void handleDownstream( + ChannelHandlerContext context, ChannelEvent evt) throws Exception { + if (!(evt instanceof MessageEvent)) { + context.sendDownstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + ChannelBufferOutputStream bout = + new ChannelBufferOutputStream(dynamicBuffer(estimatedLength)); + bout.write(LENGTH_PLACEHOLDER); + ObjectOutputStream oout = new CompactObjectOutputStream(bout); + oout.writeObject(e.getMessage()); + oout.flush(); + oout.close(); + + ChannelBuffer msg = bout.buffer(); + msg.setInt(0, msg.writerIndex() - 4); + + write(context, e.getChannel(), e.getFuture(), msg, e.getRemoteAddress()); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoderOutputStream.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoderOutputStream.java new file mode 100644 index 000000000000..74137246d0c6 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/ObjectEncoderOutputStream.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.serialization; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBufferOutputStream; +import net.gleamynode.netty.buffer.ChannelBuffers; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ObjectEncoderOutputStream extends OutputStream implements + ObjectOutput { + + private final DataOutputStream out; + private final int estimatedLength; + + public ObjectEncoderOutputStream(OutputStream out, int estimatedLength) { + if (out == null) { + throw new NullPointerException("out"); + } + if (estimatedLength < 8) { + throw new IllegalArgumentException("estimatedLength: " + estimatedLength); + } + + if (out instanceof DataOutputStream) { + this.out = (DataOutputStream) out; + } else { + this.out = new DataOutputStream(out); + } + this.estimatedLength = estimatedLength; + } + + public void writeObject(Object obj) throws IOException { + ChannelBufferOutputStream bout = new ChannelBufferOutputStream(ChannelBuffers.dynamicBuffer(estimatedLength)); + ObjectOutputStream oout = new CompactObjectOutputStream(bout); + oout.writeObject(obj); + oout.flush(); + oout.close(); + + ChannelBuffer buffer = bout.buffer(); + int objectSize = buffer.readableBytes(); + writeInt(objectSize); + buffer.getBytes(0, this, objectSize); + } + + @Override + public void write(int b) throws IOException { + out.write(b); + } + + @Override + public void close() throws IOException { + out.close(); + } + + @Override + public void flush() throws IOException { + out.flush(); + } + + public final int size() { + return out.size(); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + @Override + public void write(byte[] b) throws IOException { + out.write(b); + } + + public final void writeBoolean(boolean v) throws IOException { + out.writeBoolean(v); + } + + public final void writeByte(int v) throws IOException { + out.writeByte(v); + } + + public final void writeBytes(String s) throws IOException { + out.writeBytes(s); + } + + public final void writeChar(int v) throws IOException { + out.writeChar(v); + } + + public final void writeChars(String s) throws IOException { + out.writeChars(s); + } + + public final void writeDouble(double v) throws IOException { + out.writeDouble(v); + } + + public final void writeFloat(float v) throws IOException { + out.writeFloat(v); + } + + public final void writeInt(int v) throws IOException { + out.writeInt(v); + } + + public final void writeLong(long v) throws IOException { + out.writeLong(v); + } + + public final void writeShort(int v) throws IOException { + out.writeShort(v); + } + + public final void writeUTF(String str) throws IOException { + out.writeUTF(str); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/serialization/package-info.java b/src/main/java/net/gleamynode/netty/handler/codec/serialization/package-info.java new file mode 100644 index 000000000000..78aceab32490 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/serialization/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Encoder, decoder and their compatibility stream implementations which + * transform a {@link java.io.Serializable} object into a byte buffer and + * vice versa. + * + * @apiviz.exclude ^java\..*Stream$ + */ +package net.gleamynode.netty.handler.codec.serialization; diff --git a/src/main/java/net/gleamynode/netty/handler/codec/string/StringDecoder.java b/src/main/java/net/gleamynode/netty/handler/codec/string/StringDecoder.java new file mode 100644 index 000000000000..8ef3157469fa --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/string/StringDecoder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.string; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.nio.charset.Charset; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelUpstreamHandler; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +@ChannelPipelineCoverage("all") +public class StringDecoder implements ChannelUpstreamHandler { + + private final String charsetName; + + public StringDecoder() { + this(Charset.defaultCharset()); + } + + public StringDecoder(String charsetName) { + this(Charset.forName(charsetName)); + } + + public StringDecoder(Charset charset) { + if (charset == null) { + throw new NullPointerException("charset"); + } + charsetName = charset.name(); + } + + public void handleUpstream( + ChannelHandlerContext context, ChannelEvent evt) throws Exception { + if (!(evt instanceof MessageEvent)) { + context.sendUpstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + if (!(e.getMessage() instanceof ChannelBuffer)) { + context.sendUpstream(evt); + return; + } + + ChannelBuffer src = (ChannelBuffer) e.getMessage(); + byte[] dst = new byte[src.readableBytes()]; + src.getBytes(src.readerIndex(), dst); + fireMessageReceived(context, e.getChannel(), new String(dst, charsetName)); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/string/StringEncoder.java b/src/main/java/net/gleamynode/netty/handler/codec/string/StringEncoder.java new file mode 100644 index 000000000000..0bebbd552f4c --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/string/StringEncoder.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.codec.string; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.nio.charset.Charset; + +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.ChannelDownstreamHandler; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.MessageEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev:231 $, $Date:2008-06-12 16:44:50 +0900 (목, 12 6ì›” 2008) $ + * + */ +@ChannelPipelineCoverage("all") +public class StringEncoder implements ChannelDownstreamHandler { + + private final String charsetName; + + public StringEncoder() { + this(Charset.defaultCharset()); + } + + public StringEncoder(String charsetName) { + this(Charset.forName(charsetName)); + } + + public StringEncoder(Charset charset) { + if (charset == null) { + throw new NullPointerException("charset"); + } + charsetName = charset.name(); + } + + public void handleDownstream( + ChannelHandlerContext context, ChannelEvent evt) throws Exception { + if (!(evt instanceof MessageEvent)) { + context.sendDownstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + if (!(e.getMessage() instanceof String)) { + context.sendDownstream(evt); + return; + } + + String src = (String) e.getMessage(); + write(context, e.getChannel(), e.getFuture(), ChannelBuffers.wrappedBuffer(src.getBytes(charsetName))); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/codec/string/package-info.java b/src/main/java/net/gleamynode/netty/handler/codec/string/package-info.java new file mode 100644 index 000000000000..494ec5a5f56d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/codec/string/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Encoder and decoder which transform a string into a byte buffer and vice + * versa. + */ +package net.gleamynode.netty.handler.codec.string; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/execution/ChannelEventRunnable.java b/src/main/java/net/gleamynode/netty/handler/execution/ChannelEventRunnable.java new file mode 100644 index 000000000000..97a8cf1d7e7b --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/ChannelEventRunnable.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; + +public class ChannelEventRunnable implements Runnable { + private final ChannelHandlerContext ctx; + private final ChannelEvent e; + + public ChannelEventRunnable(ChannelHandlerContext ctx, ChannelEvent e) { + this.ctx = ctx; + this.e = e; + } + + public ChannelHandlerContext getContext() { + return ctx; + } + + public ChannelEvent getEvent() { + return e; + } + + public void run() { + ctx.sendUpstream(e); + } +} \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/execution/DefaultObjectSizeEstimator.java b/src/main/java/net/gleamynode/netty/handler/execution/DefaultObjectSizeEstimator.java new file mode 100644 index 000000000000..0a5c60086339 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/DefaultObjectSizeEstimator.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.channel.MessageEvent; + +public class DefaultObjectSizeEstimator implements ObjectSizeEstimator { + + private final ConcurrentMap, Integer> class2size = + new ConcurrentHashMap, Integer>(); + + public DefaultObjectSizeEstimator() { + class2size.put(boolean.class, 4); // Probably an integer. + class2size.put(byte.class, 1); + class2size.put(char.class, 2); + class2size.put(int.class, 4); + class2size.put(short.class, 2); + class2size.put(long.class, 8); + class2size.put(float.class, 4); + class2size.put(double.class, 8); + class2size.put(void.class, 0); + } + + public int estimateSize(Object o) { + if (o == null) { + return 8; + } + + int answer = 8 + estimateSize(o.getClass(), null); + + if (o instanceof ChannelEventRunnable) { + answer += estimateSize(((ChannelEventRunnable) o).getEvent()); + } else if (o instanceof MessageEvent) { + answer += estimateSize(((MessageEvent) o).getMessage()); + } else if (o instanceof ChannelBuffer) { + answer += ((ChannelBuffer) o).capacity(); + } else if (o instanceof byte[]) { + answer += ((byte[]) o).length; + } else if (o instanceof ByteBuffer) { + answer += ((ByteBuffer) o).remaining(); + } else if (o instanceof CharSequence) { + answer += ((CharSequence) o).length() << 1; + } else if (o instanceof Iterable) { + for (Object m : (Iterable) o) { + answer += estimateSize(m); + } + } + + return align(answer); + } + + private int estimateSize(Class clazz, Set> visitedClasses) { + Integer objectSize = class2size.get(clazz); + if (objectSize != null) { + return objectSize; + } + + if (visitedClasses != null) { + if (visitedClasses.contains(clazz)) { + return 0; + } + } else { + visitedClasses = new HashSet>(); + } + + visitedClasses.add(clazz); + + int answer = 8; // Basic overhead. + for (Class c = clazz; c != null; c = c.getSuperclass()) { + Field[] fields = c.getDeclaredFields(); + for (Field f : fields) { + if ((f.getModifiers() & Modifier.STATIC) != 0) { + // Ignore static fields. + continue; + } + + answer += estimateSize(f.getType(), visitedClasses); + } + } + + visitedClasses.remove(clazz); + + // Some alignment. + answer = align(answer); + + // Put the final answer. + class2size.putIfAbsent(clazz, answer); + return answer; + } + + private static int align(int size) { + if (size % 8 != 0) { + size /= 8; + size ++; + size *= 8; + } + return size; + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/execution/ExecutionHandler.java b/src/main/java/net/gleamynode/netty/handler/execution/ExecutionHandler.java new file mode 100644 index 000000000000..dd732e14af1d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/ExecutionHandler.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +import java.util.concurrent.Executor; + +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelPipelineCoverage; +import net.gleamynode.netty.channel.ChannelUpstreamHandler; + +/** + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.has java.util.concurrent.ThreadPoolExecutor + */ +@ChannelPipelineCoverage("all") +public class ExecutionHandler implements ChannelUpstreamHandler { + + private final Executor executor; + + public ExecutionHandler(Executor executor) { + if (executor == null) { + throw new NullPointerException("executor"); + } + this.executor = executor; + } + + public final Executor getExecutor() { + return executor; + } + + public void handleUpstream( + ChannelHandlerContext context, ChannelEvent e) throws Exception { + executor.execute(new ChannelEventRunnable(context, e)); + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/execution/MemoryAwareThreadPoolExecutor.java b/src/main/java/net/gleamynode/netty/handler/execution/MemoryAwareThreadPoolExecutor.java new file mode 100644 index 000000000000..cefa606ded0d --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/MemoryAwareThreadPoolExecutor.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelState; +import net.gleamynode.netty.channel.ChannelStateEvent; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.uses net.gleamynode.netty.handler.execution.ObjectSizeEstimator + * @apiviz.uses net.gleamynode.netty.handler.execution.ChannelEventRunnable + */ +public class MemoryAwareThreadPoolExecutor extends ThreadPoolExecutor { + + private volatile int maxChannelMemorySize; + private volatile int maxTotalMemorySize; + private final ObjectSizeEstimator objectSizeEstimator; + + private final ConcurrentMap channelCounters = + new ConcurrentHashMap(); + private final AtomicInteger totalCounter = new AtomicInteger(); + + private final Semaphore semaphore = new Semaphore(0); + + public MemoryAwareThreadPoolExecutor( + int corePoolSize, int maxChannelMemorySize, int maxTotalMemorySize) { + this(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, 30, TimeUnit.SECONDS); + } + + public MemoryAwareThreadPoolExecutor( + int corePoolSize, int maxChannelMemorySize, int maxTotalMemorySize, long keepAliveTime, TimeUnit unit) { + this(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, keepAliveTime, unit, Executors.defaultThreadFactory()); + } + + public MemoryAwareThreadPoolExecutor( + int corePoolSize, int maxChannelMemorySize, int maxTotalMemorySize, long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { + this(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, keepAliveTime, unit, new DefaultObjectSizeEstimator(), threadFactory); + } + public MemoryAwareThreadPoolExecutor( + int corePoolSize, int maxChannelMemorySize, int maxTotalMemorySize, long keepAliveTime, TimeUnit unit, ObjectSizeEstimator objectSizeEstimator, ThreadFactory threadFactory) { + super(corePoolSize, corePoolSize, keepAliveTime, unit, new LinkedBlockingQueue(), threadFactory); + + if (objectSizeEstimator == null) { + throw new NullPointerException("objectSizeEstimator"); + } + this.objectSizeEstimator = objectSizeEstimator; + + // Call allowCoreThreadTimeOut(true) using reflection + // because it's not supported in Java 5. + try { + Method m = getClass().getMethod("allowCoreThreadTimeOut", new Class[] { boolean.class }); + m.invoke(this, Boolean.TRUE); + } catch (Exception e) { + // Java 5 + } + + setMaxChannelMemorySize(maxChannelMemorySize); + setMaxTotalMemorySize(maxTotalMemorySize); + } + + public ObjectSizeEstimator getObjectSizeEstimator() { + return objectSizeEstimator; + } + + public int getMaxChannelMemorySize() { + return maxChannelMemorySize; + } + + public void setMaxChannelMemorySize(int maxChannelMemorySize) { + if (maxChannelMemorySize < 0) { + throw new IllegalArgumentException( + "maxChannelMemorySize: " + maxChannelMemorySize); + } + this.maxChannelMemorySize = maxChannelMemorySize; + } + + public int getMaxTotalMemorySize() { + return maxTotalMemorySize; + } + + public void setMaxTotalMemorySize(int maxTotalMemorySize) { + if (maxTotalMemorySize < 0) { + throw new IllegalArgumentException( + "maxTotalMemorySize: " + maxTotalMemorySize); + } + this.maxTotalMemorySize = maxTotalMemorySize; + } + + @Override + public void execute(Runnable command) { + boolean pause = increaseCounter(command); + doExecute(command); + if (pause) { + for (;;) { + try { + semaphore.acquire(); + break; + } catch (InterruptedException e) { + // Ignore. + } + } + } + } + + protected void doExecute(Runnable task) { + doUnorderedExecute(task); + } + + protected final void doUnorderedExecute(Runnable task) { + super.execute(task); + } + + @Override + public boolean remove(Runnable task) { + boolean removed = super.remove(task); + if (removed) { + decreaseCounter(task); + } + return removed; + } + + @Override + protected void beforeExecute(Thread t, Runnable r) { + super.beforeExecute(t, r); + decreaseCounter(r); + } + + private boolean increaseCounter(Runnable task) { + if (isInterestOpsEvent(task)) { + return false; + } + + int increment = getObjectSizeEstimator().estimateSize(task); + int maxTotalMemorySize = getMaxTotalMemorySize(); + int totalCounter = this.totalCounter.addAndGet(increment); + + if (task instanceof ChannelEventRunnable) { + Channel channel = ((ChannelEventRunnable) task).getEvent().getChannel(); + int maxChannelMemorySize = getMaxChannelMemorySize(); + int channelCounter = getChannelCounter(channel).addAndGet(increment); + if (maxChannelMemorySize != 0 && channelCounter >= maxChannelMemorySize && channel.isOpen()) { + if (channel.isReadable()) { + channel.setReadable(false); + } + } + } + + return maxTotalMemorySize != 0 && totalCounter >= maxTotalMemorySize; + } + + private void decreaseCounter(Runnable task) { + if (isInterestOpsEvent(task)) { + return; + } + + int increment = getObjectSizeEstimator().estimateSize(task); + int maxTotalMemorySize = getMaxTotalMemorySize(); + int totalCounter = this.totalCounter.addAndGet(-increment); + + if (maxTotalMemorySize == 0 || totalCounter < maxTotalMemorySize) { + semaphore.release(); + } + + if (task instanceof ChannelEventRunnable) { + Channel channel = ((ChannelEventRunnable) task).getEvent().getChannel(); + int maxChannelMemorySize = getMaxChannelMemorySize(); + int channelCounter = getChannelCounter(channel).addAndGet(-increment); + if ((maxChannelMemorySize == 0 || channelCounter < maxChannelMemorySize) && channel.isOpen()) { + if (!channel.isReadable()) { + channel.setReadable(true); + } + } + } + } + + private AtomicInteger getChannelCounter(Channel channel) { + AtomicInteger counter = channelCounters.get(channel); + if (counter == null) { + counter = new AtomicInteger(); + AtomicInteger oldCounter = channelCounters.putIfAbsent(channel, counter); + if (oldCounter != null) { + counter = oldCounter; + } + } + + // Remove the entry when the channel closes. + if (!channel.isOpen()) { + channelCounters.remove(channel); + } + return counter; + } + + private static boolean isInterestOpsEvent(Runnable task) { + if (task instanceof ChannelEventRunnable) { + ChannelEventRunnable r = (ChannelEventRunnable) task; + if (r.getEvent() instanceof ChannelStateEvent) { + ChannelStateEvent e = (ChannelStateEvent) r.getEvent(); + if (e.getState() == ChannelState.INTEREST_OPS) { + return true; + } + } + } + return false; + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/execution/ObjectSizeEstimator.java b/src/main/java/net/gleamynode/netty/handler/execution/ObjectSizeEstimator.java new file mode 100644 index 000000000000..6d99f374efe4 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/ObjectSizeEstimator.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +public interface ObjectSizeEstimator { + int estimateSize(Object o); +} diff --git a/src/main/java/net/gleamynode/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java b/src/main/java/net/gleamynode/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java new file mode 100644 index 000000000000..15cbddab9a02 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.execution; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import net.gleamynode.netty.channel.Channel; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * @author David M. Lloyd (david.lloyd@redhat.com) + * + * @version $Rev$, $Date$ + * + */ +public class OrderedMemoryAwareThreadPoolExecutor extends + MemoryAwareThreadPoolExecutor { + + private final ConcurrentMap childExecutors = + new ConcurrentHashMap(); + + public OrderedMemoryAwareThreadPoolExecutor(int corePoolSize, + int maxChannelMemorySize, int maxTotalMemorySize, + long keepAliveTime, TimeUnit unit, + ObjectSizeEstimator objectSizeEstimator, ThreadFactory threadFactory) { + super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, + keepAliveTime, unit, objectSizeEstimator, threadFactory); + } + + public OrderedMemoryAwareThreadPoolExecutor(int corePoolSize, + int maxChannelMemorySize, int maxTotalMemorySize, + long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { + super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, + keepAliveTime, unit, threadFactory); + } + + public OrderedMemoryAwareThreadPoolExecutor(int corePoolSize, + int maxChannelMemorySize, int maxTotalMemorySize, + long keepAliveTime, TimeUnit unit) { + super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize, + keepAliveTime, unit); + } + + public OrderedMemoryAwareThreadPoolExecutor(int corePoolSize, + int maxChannelMemorySize, int maxTotalMemorySize) { + super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize); + } + + @Override + protected void doExecute(Runnable task) { + if (!(task instanceof ChannelEventRunnable)) { + doUnorderedExecute(task); + } else { + ChannelEventRunnable r = (ChannelEventRunnable) task; + getOrderedExecutor(r.getEvent().getChannel()).execute(task); + } + } + + private Executor getOrderedExecutor(Channel channel) { + Executor executor = childExecutors.get(channel); + if (executor == null) { + executor = new ChildExecutor(); + Executor oldExecutor = childExecutors.putIfAbsent(channel, executor); + if (oldExecutor != null) { + executor = oldExecutor; + } + } + + // Remove the entry when the channel closes. + if (!channel.isOpen()) { + childExecutors.remove(channel); + } + return executor; + } + + private class ChildExecutor implements Executor, Runnable { + private final Set runningChildren = new HashSet(); + private final Queue tasks = new LinkedList(); + + ChildExecutor() { + super(); + } + + public void execute(Runnable command) { + synchronized (tasks) { + tasks.add(command); + if (tasks.size() == 1 && runningChildren.add(this)) { + doUnorderedExecute(this); + } + } + } + + public void run() { + for (;;) { + final Runnable task; + synchronized (tasks) { + task = tasks.poll(); + if (task == null) { + runningChildren.remove(this); + return; + } + } + task.run(); + } + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/execution/package-info.java b/src/main/java/net/gleamynode/netty/handler/execution/package-info.java new file mode 100644 index 000000000000..f770334a14db --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/execution/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * {@link java.util.concurrent.Executor}-based implementation of various + * thread models and memory overload prevention mechanisms. + * + * @apiviz.exclude ^java\.lang\. + */ +package net.gleamynode.netty.handler.execution; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/handler/ssl/SslBufferPool.java b/src/main/java/net/gleamynode/netty/handler/ssl/SslBufferPool.java new file mode 100644 index 000000000000..a7e187973e99 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/ssl/SslBufferPool.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.ssl; + +import java.nio.ByteBuffer; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SslBufferPool { + + // Add 1024 as a room for compressed data. + private static final int MAX_PACKET_SIZE = 16665 + 1024; + private static final int DEFAULT_POOL_SIZE = MAX_PACKET_SIZE * 1024; + + private final ByteBuffer[] pool; + private final int maxBufferCount; + private int index; + + public SslBufferPool() { + this(DEFAULT_POOL_SIZE); + } + + public SslBufferPool(int poolSize) { + if (poolSize <= 0) { + throw new IllegalArgumentException("poolSize: " + poolSize); + } + + int maxBufferCount = poolSize / MAX_PACKET_SIZE; + if (poolSize % MAX_PACKET_SIZE != 0) { + maxBufferCount ++; + } + poolSize = maxBufferCount * MAX_PACKET_SIZE; + + pool = new ByteBuffer[maxBufferCount]; + this.maxBufferCount = maxBufferCount; + } + + public int getMaxPoolSize() { + return maxBufferCount * MAX_PACKET_SIZE; + } + + public synchronized int getUnacquiredPoolSize() { + return index * MAX_PACKET_SIZE; + } + + synchronized ByteBuffer acquire() { + if (index == 0) { + return ByteBuffer.allocate(MAX_PACKET_SIZE); + } else { + return (ByteBuffer) pool[-- index].clear(); + } + } + + synchronized void release(ByteBuffer buffer) { + if (index < maxBufferCount) { + pool[index ++] = buffer; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/ssl/SslHandler.java b/src/main/java/net/gleamynode/netty/handler/ssl/SslHandler.java new file mode 100644 index 000000000000..63775f2676f9 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/ssl/SslHandler.java @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.handler.ssl; + +import static net.gleamynode.netty.channel.Channels.*; + +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLEngineResult.Status; + +import net.gleamynode.netty.buffer.ChannelBuffer; +import net.gleamynode.netty.buffer.ChannelBuffers; +import net.gleamynode.netty.channel.Channel; +import net.gleamynode.netty.channel.ChannelDownstreamHandler; +import net.gleamynode.netty.channel.ChannelEvent; +import net.gleamynode.netty.channel.ChannelFuture; +import net.gleamynode.netty.channel.ChannelFutureListener; +import net.gleamynode.netty.channel.ChannelHandlerContext; +import net.gleamynode.netty.channel.ChannelStateEvent; +import net.gleamynode.netty.channel.Channels; +import net.gleamynode.netty.channel.MessageEvent; +import net.gleamynode.netty.handler.codec.frame.FrameDecoder; +import net.gleamynode.netty.util.ImmediateExecutor; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + * @apiviz.landmark + * @apiviz.uses net.gleamynode.netty.handler.ssl.SslBufferPool + */ +public class SslHandler extends FrameDecoder implements ChannelDownstreamHandler { + + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + + private static SslBufferPool defaultBufferPool; + + private static synchronized SslBufferPool getDefaultBufferPool() { + if (defaultBufferPool == null) { + defaultBufferPool = new SslBufferPool(); + } + return defaultBufferPool; + } + + private final SSLEngine engine; + private final SslBufferPool bufferPool; + private final Executor delegatedTaskExecutor; + private final boolean startTls; + + private final Object handshakeLock = new Object(); + private volatile boolean handshaking; + private volatile boolean handshaken; + private volatile ChannelFuture handshakeFuture; + + private final AtomicBoolean sentFirstMessage = new AtomicBoolean(); + private final AtomicBoolean sentCloseNotify = new AtomicBoolean(); + final Queue closeFutures = new ConcurrentLinkedQueue(); + private final Queue pendingUnencryptedWrites = new LinkedList(); + private final Queue pendingEncryptedWrites = new LinkedList(); + + public SslHandler(SSLEngine engine) { + this(engine, getDefaultBufferPool(), ImmediateExecutor.INSTANCE); + } + + public SslHandler(SSLEngine engine, SslBufferPool bufferPool) { + this(engine, bufferPool, ImmediateExecutor.INSTANCE); + } + + public SslHandler(SSLEngine engine, boolean startTls) { + this(engine, getDefaultBufferPool(), startTls); + } + + public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls) { + this(engine, bufferPool, startTls, ImmediateExecutor.INSTANCE); + } + + public SslHandler(SSLEngine engine, Executor delegatedTaskExecutor) { + this(engine, getDefaultBufferPool(), delegatedTaskExecutor); + } + + public SslHandler(SSLEngine engine, SslBufferPool bufferPool, Executor delegatedTaskExecutor) { + this(engine, bufferPool, false, delegatedTaskExecutor); + } + + public SslHandler(SSLEngine engine, boolean startTls, Executor delegatedTaskExecutor) { + this(engine, getDefaultBufferPool(), startTls, delegatedTaskExecutor); + } + + public SslHandler(SSLEngine engine, SslBufferPool bufferPool, boolean startTls, Executor delegatedTaskExecutor) { + if (engine == null) { + throw new NullPointerException("engine"); + } + if (bufferPool == null) { + throw new NullPointerException("bufferPool"); + } + if (delegatedTaskExecutor == null) { + throw new NullPointerException("delegatedTaskExecutor"); + } + this.engine = engine; + this.bufferPool = bufferPool; + this.delegatedTaskExecutor = delegatedTaskExecutor; + this.startTls = startTls; + } + + public SSLEngine getEngine() { + return engine; + } + + public ChannelFuture handshake(Channel channel) throws SSLException { + ChannelFuture handshakeFuture; + synchronized (handshakeLock) { + if (handshaking) { + return this.handshakeFuture; + } else { + handshakeFuture = this.handshakeFuture = future(channel); + handshaking = true; + } + } + + ChannelHandlerContext ctx = context(channel); + engine.beginHandshake(); + wrapNonAppData(ctx, channel); + return handshakeFuture; + } + + public ChannelFuture close(Channel channel) throws SSLException { + ChannelHandlerContext ctx = context(channel); + engine.closeOutbound(); + return wrapNonAppData(ctx, channel); + } + + private ChannelHandlerContext context(Channel channel) { + return channel.getPipeline().getContext(getClass()); + } + + public void handleDownstream( + final ChannelHandlerContext context, final ChannelEvent evt) throws Exception { + if (evt instanceof ChannelStateEvent) { + ChannelStateEvent e = (ChannelStateEvent) evt; + switch (e.getState()) { + case OPEN: + case CONNECTED: + case BOUND: + if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) { + closeOutboundAndChannel(context, e); + return; + } + } + } + if (!(evt instanceof MessageEvent)) { + context.sendDownstream(evt); + return; + } + + MessageEvent e = (MessageEvent) evt; + if (!(e.getMessage() instanceof ChannelBuffer)) { + context.sendDownstream(evt); + return; + } + + // Don't encrypt the first write request if this handler is + // created with startTLS flag turned on. + if (startTls && sentFirstMessage.compareAndSet(false, true)) { + context.sendDownstream(evt); + return; + } + + // Otherwise, all messages are encrypted. + ChannelBuffer msg = (ChannelBuffer) e.getMessage(); + PendingWrite pendingWrite = + new PendingWrite(evt.getFuture(), msg.toByteBuffer(msg.readerIndex(), msg.readableBytes())); + synchronized (pendingUnencryptedWrites) { + pendingUnencryptedWrites.offer(pendingWrite); + } + + wrap(context, evt.getChannel()); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, + ChannelStateEvent e) throws Exception { + super.channelDisconnected(ctx, e); + unwrap(ctx, e.getChannel(), ChannelBuffer.EMPTY_BUFFER, 0, 0); + engine.closeOutbound(); + if (!sentCloseNotify.get() && handshaken) { + try { + engine.closeInbound(); + } catch (SSLException ex) { + ex.printStackTrace(); + } + } + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + if (buffer.readableBytes() < 2) { + return null; + } + + int packetLength = buffer.getShort(buffer.readerIndex()) & 0xFFFF; + if ((packetLength & 0x8000) != 0) { + // Detected a SSLv2 packet + packetLength &= 0x7FFF; + packetLength += 2; + } else if (buffer.readableBytes() < 5) { + return null; + } else { + // Detected a SSLv3 / TLSv1 packet + packetLength = (buffer.getShort(buffer.readerIndex() + 3) & 0xFFFF) + 5; + } + + if (buffer.readableBytes() < packetLength) { + return null; + } + + ChannelBuffer frame; + try { + frame = unwrap(ctx, channel, buffer, buffer.readerIndex(), packetLength); + } finally { + buffer.skipBytes(packetLength); + } + + if (frame == null && engine.isInboundDone()) { + for (;;) { + ChannelFuture future = closeFutures.poll(); + if (future == null) { + break; + } + Channels.close(ctx, channel, future); + } + } + return frame; + } + + private ChannelFuture wrap(ChannelHandlerContext context, Channel channel) + throws SSLException { + + ChannelFuture future = null; + ChannelBuffer msg; + ByteBuffer outNetBuf = bufferPool.acquire(); + boolean success = true; + try { + loop: + for (;;) { + // Acquire a lock to make sure unencrypted data is polled + // in order and their encrypted counterpart is offered in + // order. + synchronized (pendingUnencryptedWrites) { + PendingWrite pendingWrite = pendingUnencryptedWrites.peek(); + if (pendingWrite == null) { + break; + } + + ByteBuffer outAppBuf = pendingWrite.outAppBuf; + + SSLEngineResult result; + try { + result = engine.wrap(outAppBuf, outNetBuf); + } finally { + if (!outAppBuf.hasRemaining()) { + pendingUnencryptedWrites.remove(); + } + } + if (result.bytesProduced() > 0) { + outNetBuf.flip(); + msg = ChannelBuffers.buffer(outNetBuf.remaining()); + msg.writeBytes(outNetBuf.array(), 0, msg.capacity()); + outNetBuf.clear(); + + if (pendingWrite.outAppBuf.hasRemaining()) { + // pendingWrite's future shouldn't be notified if + // only partial data is written. + future = succeededFuture(channel); + } else { + future = pendingWrite.future; + } + + MessageEvent encryptedWrite = messageEvent(channel, future, msg); + if (Thread.holdsLock(pendingEncryptedWrites)) { + pendingEncryptedWrites.offer(encryptedWrite); + } else { + synchronized (pendingEncryptedWrites) { + pendingEncryptedWrites.offer(encryptedWrite); + } + } + } else { + switch (result.getHandshakeStatus()) { + case NEED_WRAP: + if (outAppBuf.hasRemaining()) { + break; + } else { + break loop; + } + case NEED_UNWRAP: + break loop; + case NEED_TASK: + runDelegatedTasks(); + break; + case FINISHED: + setHandshakeSuccess(); + default: + if (result.getStatus() == Status.CLOSED) { + success = false; + } + break loop; + } + } + } + } + } catch (SSLException e) { + success = false; + if (handshaking) { + setHandshakeFailure(e); + } + throw e; + } finally { + bufferPool.release(outNetBuf); + + flushPendingEncryptedWrites(context); + + if (!success) { + // Mark all remaining pending writes as failure if anything + // wrong happened before the write requests are wrapped. + // Please note that we don't call setFailure while a lock is + // acquired, to avoid a potential dead lock. + for (;;) { + PendingWrite pendingWrite; + synchronized (pendingUnencryptedWrites) { + pendingWrite = pendingUnencryptedWrites.poll(); + if (pendingWrite == null) { + break; + } + } + + pendingWrite.future.setFailure( + new IllegalStateException("SSLEngine already closed")); + } + } + } + + if (future == null) { + future = succeededFuture(channel); + } + return future; + } + + private void flushPendingEncryptedWrites(ChannelHandlerContext ctx) { + // Avoid possible dead lock and data integrity issue + // which is caused by cross communication between more than one channel + // in the same VM. + if (Thread.holdsLock(pendingEncryptedWrites)) { + return; + } + + synchronized (pendingEncryptedWrites) { + MessageEvent e; + while ((e = pendingEncryptedWrites.poll()) != null) { + ctx.sendDownstream(e); + } + } + } + + private ChannelFuture wrapNonAppData(ChannelHandlerContext ctx, Channel channel) throws SSLException { + ChannelFuture future = null; + ByteBuffer outNetBuf = bufferPool.acquire(); + + SSLEngineResult result; + try { + for (;;) { + result = engine.wrap(EMPTY_BUFFER, outNetBuf); + + if (result.bytesProduced() > 0) { + outNetBuf.flip(); + ChannelBuffer msg = ChannelBuffers.buffer(outNetBuf.remaining()); + msg.writeBytes(outNetBuf.array(), 0, msg.capacity()); + outNetBuf.clear(); + if (channel.isConnected()) { + future = future(channel); + write(ctx, channel, future, msg); + } + } + + switch (result.getHandshakeStatus()) { + case FINISHED: + setHandshakeSuccess(); + break; + case NEED_TASK: + runDelegatedTasks(); + break; + } + + if (result.bytesProduced() == 0) { + break; + } + } + } catch (SSLException e) { + if (handshaking) { + setHandshakeFailure(e); + } + throw e; + } finally { + bufferPool.release(outNetBuf); + } + + if (future == null) { + future = succeededFuture(channel); + } + return future; + } + + private ChannelBuffer unwrap( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, int offset, int length) throws SSLException { + ByteBuffer inNetBuf = buffer.toByteBuffer(offset, length); + ByteBuffer outAppBuf = bufferPool.acquire(); + + try { + loop: + for (;;) { + SSLEngineResult result = engine.unwrap(inNetBuf, outAppBuf); + + switch (result.getHandshakeStatus()) { + case NEED_UNWRAP: + if (inNetBuf.hasRemaining()) { + break; + } else { + break loop; + } + case NEED_WRAP: + wrapNonAppData(ctx, channel); + break; + case NEED_TASK: + runDelegatedTasks(); + break; + case FINISHED: + setHandshakeSuccess(); + case NOT_HANDSHAKING: + wrap(ctx, channel); + break loop; + default: + break loop; + } + } + + outAppBuf.flip(); + + if (outAppBuf.hasRemaining()) { + ChannelBuffer frame = ChannelBuffers.buffer(outAppBuf.remaining()); + frame.writeBytes(outAppBuf.array(), 0, frame.capacity()); + return frame; + } else { + return null; + } + } catch (SSLException e) { + if (handshaking) { + setHandshakeFailure(e); + } + throw e; + } finally { + bufferPool.release(outAppBuf); + } + } + + private void runDelegatedTasks() { + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + delegatedTaskExecutor.execute(task); + } + } + + private void setHandshakeSuccess() { + synchronized (handshakeLock) { + handshaking = false; + handshaken = true; + } + handshakeFuture.setSuccess(); + } + + private void setHandshakeFailure(SSLException cause) { + synchronized (handshakeLock) { + handshaking = false; + handshaken = false; + } + handshakeFuture.setFailure(cause); + } + + private void closeOutboundAndChannel( + final ChannelHandlerContext context, final ChannelStateEvent e) throws SSLException { + unwrap(context, e.getChannel(), ChannelBuffer.EMPTY_BUFFER, 0, 0); + if (!engine.isInboundDone()) { + if (sentCloseNotify.compareAndSet(false, true)) { + engine.closeOutbound(); + ChannelFuture closeNotifyFuture = wrapNonAppData(context, e.getChannel()); + closeNotifyFuture.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture closeNotifyFuture) throws Exception { + closeFutures.offer(e.getFuture()); + } + }); + return; + } + } + + context.sendDownstream(e); + } + + private static class PendingWrite { + final ChannelFuture future; + final ByteBuffer outAppBuf; + + PendingWrite(ChannelFuture future, ByteBuffer outAppBuf) { + this.future = future; + this.outAppBuf = outAppBuf; + } + } +} diff --git a/src/main/java/net/gleamynode/netty/handler/ssl/package-info.java b/src/main/java/net/gleamynode/netty/handler/ssl/package-info.java new file mode 100644 index 000000000000..e3b7d579e2e3 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/handler/ssl/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * SSL · + * TLS implementation. + */ +package net.gleamynode.netty.handler.ssl; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/logging/CommonsLoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/CommonsLoggerFactory.java new file mode 100644 index 000000000000..e62435a8b8fc --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/CommonsLoggerFactory.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class CommonsLoggerFactory extends LoggerFactory { + + @Override + public Logger getLogger(String name) { + final org.apache.commons.logging.Log logger = + org.apache.commons.logging.LogFactory.getLog(name); + return new Logger() { + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String msg, Throwable cause) { + logger.debug(msg, cause); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Throwable cause) { + logger.error(msg, cause); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String msg, Throwable cause) { + logger.info(msg, cause); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String msg, Throwable cause) { + logger.warn(msg, cause); + } + + @Override + public String toString() { + return logger.toString(); + } + }; + } +} diff --git a/src/main/java/net/gleamynode/netty/logging/JBossLoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/JBossLoggerFactory.java new file mode 100644 index 000000000000..ec1dfda772cf --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/JBossLoggerFactory.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class JBossLoggerFactory extends LoggerFactory { + + @Override + public Logger getLogger(String name) { + final org.jboss.logging.Logger logger = + org.jboss.logging.Logger.getLogger(name); + return new Logger() { + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String msg, Throwable cause) { + logger.debug(msg, cause); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Throwable cause) { + logger.error(msg, cause); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String msg, Throwable cause) { + logger.info(msg, cause); + } + + @Override + @SuppressWarnings("deprecation") + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String msg, Throwable cause) { + logger.warn(msg, cause); + } + + @Override + public String toString() { + return String.valueOf(logger.getName()); + } + }; + } +} diff --git a/src/main/java/net/gleamynode/netty/logging/JdkLoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/JdkLoggerFactory.java new file mode 100644 index 000000000000..f59f6d9f82ce --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/JdkLoggerFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + +import java.util.logging.Level; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class JdkLoggerFactory extends LoggerFactory { + + @Override + public Logger getLogger(String name) { + final java.util.logging.Logger logger = + java.util.logging.Logger.getLogger(name); + return new Logger() { + @Override + public void debug(String msg) { + logger.log(Level.FINE, msg); + } + + @Override + public void debug(String msg, Throwable cause) { + logger.log(Level.FINE, msg, cause); + } + + @Override + public void error(String msg) { + logger.log(Level.SEVERE, msg); + } + + @Override + public void error(String msg, Throwable cause) { + logger.log(Level.SEVERE, msg, cause); + } + + @Override + public void info(String msg) { + logger.log(Level.INFO, msg); + } + + @Override + public void info(String msg, Throwable cause) { + logger.log(Level.INFO, msg, cause); + } + + @Override + public boolean isDebugEnabled() { + return logger.isLoggable(Level.FINE); + } + + @Override + public boolean isErrorEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + @Override + public boolean isInfoEnabled() { + return logger.isLoggable(Level.INFO); + } + + @Override + public boolean isWarnEnabled() { + return logger.isLoggable(Level.WARNING); + } + + @Override + public void warn(String msg) { + logger.log(Level.WARNING, msg); + } + + @Override + public void warn(String msg, Throwable cause) { + logger.log(Level.WARNING, msg, cause); + } + + @Override + public String toString() { + return String.valueOf(logger.getName()); + } + }; + } +} diff --git a/src/main/java/net/gleamynode/netty/logging/Log4JLoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/Log4JLoggerFactory.java new file mode 100644 index 000000000000..7b37a3bc1aa0 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/Log4JLoggerFactory.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class Log4JLoggerFactory extends LoggerFactory { + + @Override + public Logger getLogger(String name) { + final org.apache.log4j.Logger logger = + org.apache.log4j.Logger.getLogger(name); + return new Logger() { + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String msg, Throwable cause) { + logger.debug(msg, cause); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Throwable cause) { + logger.error(msg, cause); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String msg, Throwable cause) { + logger.info(msg, cause); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String msg, Throwable cause) { + logger.warn(msg, cause); + } + + @Override + public String toString() { + return String.valueOf(logger.getName()); + } + }; + } +} diff --git a/src/main/java/net/gleamynode/netty/logging/Logger.java b/src/main/java/net/gleamynode/netty/logging/Logger.java new file mode 100644 index 000000000000..80c19c4f2c63 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/Logger.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public abstract class Logger { + public static Logger getLogger(Class clazz) { + return getLogger(clazz.getName()); + } + + public static Logger getLogger(String name) { + return LoggerFactory.getDefault().getLogger(name); + } + + public abstract boolean isDebugEnabled(); + public abstract boolean isInfoEnabled(); + public abstract boolean isWarnEnabled(); + public abstract boolean isErrorEnabled(); + + public abstract void debug(String msg); + public abstract void debug(String msg, Throwable cause); + public abstract void info(String msg); + public abstract void info(String msg, Throwable cause); + public abstract void warn(String msg); + public abstract void warn(String msg, Throwable cause); + public abstract void error(String msg); + public abstract void error(String msg, Throwable cause); +} diff --git a/src/main/java/net/gleamynode/netty/logging/LoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/LoggerFactory.java new file mode 100644 index 000000000000..c0fb76e78470 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/LoggerFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public abstract class LoggerFactory { + private static volatile LoggerFactory defaultInstance = new JdkLoggerFactory(); + + public static LoggerFactory getDefault() { + return defaultInstance; + } + + public static void setDefault(LoggerFactory defaultInstance) { + if (defaultInstance == null) { + throw new NullPointerException("defaultInstance"); + } + LoggerFactory.defaultInstance = defaultInstance; + } + + public abstract Logger getLogger(String name); +} diff --git a/src/main/java/net/gleamynode/netty/logging/Slf4JLoggerFactory.java b/src/main/java/net/gleamynode/netty/logging/Slf4JLoggerFactory.java new file mode 100644 index 000000000000..6e81512a83aa --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/Slf4JLoggerFactory.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.logging; + + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class Slf4JLoggerFactory extends LoggerFactory { + + @Override + public Logger getLogger(String name) { + final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(name); + return new Logger() { + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void debug(String msg, Throwable cause) { + logger.debug(msg, cause); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Throwable cause) { + logger.error(msg, cause); + } + + @Override + public void info(String msg) { + logger.info(msg); + } + + @Override + public void info(String msg, Throwable cause) { + logger.info(msg, cause); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void warn(String msg, Throwable cause) { + logger.warn(msg, cause); + } + + @Override + public String toString() { + return String.valueOf(logger.getName()); + } + }; + } +} diff --git a/src/main/java/net/gleamynode/netty/logging/package-info.java b/src/main/java/net/gleamynode/netty/logging/package-info.java new file mode 100644 index 000000000000..2901c8736316 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/logging/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ + +/** + * Simplistic internal-use-only logging layer which allows a user to + * decide what logging framework Netty should use to log its own messages. + */ +package net.gleamynode.netty.logging; \ No newline at end of file diff --git a/src/main/java/net/gleamynode/netty/util/ConvertUtil.java b/src/main/java/net/gleamynode/netty/util/ConvertUtil.java new file mode 100644 index 000000000000..f3ce72219554 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/ConvertUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ConvertUtil { + + public static int toInt(Object value) { + if (value instanceof Number) { + return ((Number) value).intValue(); + } else { + return Integer.parseInt(String.valueOf(value)); + } + } + + public static boolean toBoolean(Object value) { + if (value instanceof Boolean) { + return ((Boolean) value).booleanValue(); + } + if (value instanceof Number) { + return ((Number) value).intValue() != 0; + } else { + String s = String.valueOf(value); + if (s.length() == 0) { + return false; + } + + try { + return Integer.parseInt(s) != 0; + } catch (NumberFormatException e) { + // Proceed + } + + switch (Character.toUpperCase(s.charAt(0))) { + case 'T': case 'Y': + return true; + } + return false; + } + } + + public static int toPowerOfTwo(int value) { + if (value <= 0) { + return 0; + } + int newValue = 1; + while (newValue < value) { + newValue <<= 1; + if (newValue > 0) { + return 0x40000000; + } + } + return newValue; + } + + private ConvertUtil() { + // Unused + } +} diff --git a/src/main/java/net/gleamynode/netty/util/ImmediateExecutor.java b/src/main/java/net/gleamynode/netty/util/ImmediateExecutor.java new file mode 100644 index 000000000000..4688011002b3 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/ImmediateExecutor.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +import java.util.concurrent.Executor; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ImmediateExecutor implements Executor { + + public static final ImmediateExecutor INSTANCE = new ImmediateExecutor(); + + public void execute(Runnable command) { + command.run(); + } +} diff --git a/src/main/java/net/gleamynode/netty/util/MapBackedSet.java b/src/main/java/net/gleamynode/netty/util/MapBackedSet.java new file mode 100644 index 000000000000..f8cb08b06aca --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/MapBackedSet.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * A {@link Map}-backed {@link Set}. + * + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + */ +public class MapBackedSet extends AbstractSet implements Serializable { + + private static final long serialVersionUID = -6761513279741915432L; + + private final Map map; + + public MapBackedSet(Map map) { + this.map = map; + } + + public MapBackedSet(Map map, Collection c) { + this.map = map; + addAll(c); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean contains(Object o) { + return map.containsKey(o); + } + + @Override + public boolean add(E o) { + return map.put(o, Boolean.TRUE) == null; + } + + @Override + public boolean remove(Object o) { + return map.remove(o) != null; + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Iterator iterator() { + return map.keySet().iterator(); + } +} diff --git a/src/main/java/net/gleamynode/netty/util/NamePreservingRunnable.java b/src/main/java/net/gleamynode/netty/util/NamePreservingRunnable.java new file mode 100644 index 000000000000..463d5a7ea5a8 --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/NamePreservingRunnable.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +import net.gleamynode.netty.logging.Logger; + +public class NamePreservingRunnable implements Runnable { + private static final Logger logger = + Logger.getLogger(NamePreservingRunnable.class); + + private final String newName; + private final Runnable runnable; + + public NamePreservingRunnable(Runnable runnable, String newName) { + this.runnable = runnable; + this.newName = newName; + } + + public void run() { + Thread currentThread = Thread.currentThread(); + String oldName = currentThread.getName(); + + if (newName != null) { + setName(currentThread, newName); + } + + try { + runnable.run(); + } finally { + setName(currentThread, oldName); + } + } + + /** + * Wraps {@link Thread#setName(String)} to catch a possible {@link Exception}s such as + * {@link SecurityException} in sandbox environments, such as applets + */ + private void setName(Thread thread, String name) { + try { + thread.setName(name); + } catch (Exception e) { + // Probably SecurityException. + logger.warn( + "Failed to set the current thread name.", e); + } + } +} diff --git a/src/main/java/net/gleamynode/netty/util/SwitchableInputStream.java b/src/main/java/net/gleamynode/netty/util/SwitchableInputStream.java new file mode 100644 index 000000000000..5a199492d0cb --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/SwitchableInputStream.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +import java.io.FilterInputStream; +import java.io.InputStream; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class SwitchableInputStream extends FilterInputStream { + + public SwitchableInputStream() { + super(null); + } + + public void switchStream(InputStream in) { + this.in = in; + } +} diff --git a/src/main/java/net/gleamynode/netty/util/TimeBasedUuidGenerator.java b/src/main/java/net/gleamynode/netty/util/TimeBasedUuidGenerator.java new file mode 100644 index 000000000000..3f98434d60de --- /dev/null +++ b/src/main/java/net/gleamynode/netty/util/TimeBasedUuidGenerator.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.util; + +import java.io.UnsupportedEncodingException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.InetAddress; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class TimeBasedUuidGenerator { + private static final AtomicInteger SEQUENCE = new AtomicInteger((int) System.nanoTime()); + private static final long NODE; + + static { + // Generate nodeKey - we can't use MAC address to support Java 5. + StringBuilder nodeKey = new StringBuilder(1024); + + //// Append host / IP address information. + try { + InetAddress localhost = InetAddress.getLocalHost(); + nodeKey.append(localhost.getCanonicalHostName()); + nodeKey.append(':'); + nodeKey.append(String.valueOf(localhost.getHostAddress())); + } catch (Exception e) { + nodeKey.append("localhost:127.0.0.1"); + } + + //// Append standard system properties. + appendSystemProperty(nodeKey, "java.version"); + appendSystemProperty(nodeKey, "java.home"); + appendSystemProperty(nodeKey, "java.vm.version"); + appendSystemProperty(nodeKey, "java.vm.vendor"); + appendSystemProperty(nodeKey, "java.vm.name"); + appendSystemProperty(nodeKey, "os.name"); + appendSystemProperty(nodeKey, "os.arch"); + appendSystemProperty(nodeKey, "os.version"); + appendSystemProperty(nodeKey, "user.name"); + + //// Append the information from java.lang.Runtime. + nodeKey.append(':'); + nodeKey.append(Runtime.getRuntime().availableProcessors()); + + //// Finally, append the another distinguishable string (probably PID.) + try { + RuntimeMXBean rtb = ManagementFactory.getRuntimeMXBean(); + nodeKey.append(':'); + nodeKey.append(rtb.getName()); + } catch (Exception e) { + // Ignore. + } + + // Generate the digest of the nodeKey. + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new InternalError("MD5 not supported"); + } + + byte[] nodeKeyDigest; + try { + nodeKeyDigest = md.digest(nodeKey.toString().getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new Error("UTF-8 is not found"); + } + + // Choose 5 bytes from the digest. + // Please note that the first byte is always 1 (multicast address.) + long node = 1; + node = node << 8 | nodeKeyDigest[1] & 0xFF; + node = node << 8 | nodeKeyDigest[4] & 0xFF; + node = node << 8 | nodeKeyDigest[7] & 0xFF; + node = node << 8 | nodeKeyDigest[10] & 0xFF; + node = node << 8 | nodeKeyDigest[13] & 0xFF; + + // We're done. + NODE = node; + } + + private static void appendSystemProperty(StringBuilder buf, String key) { + buf.append(':'); + buf.append(getSystemProperty(key)); + } + + private static String getSystemProperty(String key) { + try { + return System.getProperty(key, "null"); + } catch (Exception e) { + return "null"; + } + } + + public static UUID generate() { + long time = System.currentTimeMillis(); + int clockSeq = TimeBasedUuidGenerator.SEQUENCE.getAndIncrement(); + + long msb = (time & 0xFFFFFFFFL) << 32 | (time >>> 32 & 0xFFFF) << 16 | + time >>> 48 & 0xFFFF; + long lsb = (long) clockSeq << 48 | NODE; + + // Set to version 1 (i.e. time-based UUID) + msb = msb & 0xFFFFFFFFFFFF0FFFL | 0x0000000000001000L; + + // Set to IETF variant + lsb = lsb & 0x3FFFFFFFFFFFFFFFL | 0x8000000000000000L; + + return new UUID(msb, lsb); + } + + private TimeBasedUuidGenerator() { + // Unused + } +} diff --git a/src/site/template.vm b/src/site/template.vm new file mode 100644 index 000000000000..aa1468f265f6 --- /dev/null +++ b/src/site/template.vm @@ -0,0 +1,13 @@ + + + + $project.name - Redirecting... + + + + + Please click here if not redirected automatically. + + + + diff --git a/src/test/java/net/gleamynode/netty/buffer/AbstractChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/AbstractChannelBufferTest.java new file mode 100644 index 000000000000..89b5724cafde --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/AbstractChannelBufferTest.java @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static net.gleamynode.netty.buffer.ChannelBuffers.*; +import static org.junit.Assert.*; + +import java.nio.ByteBuffer; +import java.util.Random; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public abstract class AbstractChannelBufferTest { + + private static final int CAPACITY = 4096; + private static final int BLOCK_SIZE = 128; + + private long seed; + private Random random; + private ChannelBuffer buffer; + + protected abstract ChannelBuffer newBuffer(int capacity); + protected abstract ChannelBuffer[] components(); + + @Before + public void init() { + buffer = newBuffer(CAPACITY); + seed = System.currentTimeMillis(); + random = new Random(seed); + } + + @After + public void dispose() { + buffer = null; + } + + @Test + public void initialState() { + assertEquals(CAPACITY, buffer.capacity()); + assertEquals(0, buffer.readerIndex()); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void readerIndexBoundaryCheck1() { + try { + buffer.writerIndex(0); + } catch (IndexOutOfBoundsException e) { + fail(); + } + buffer.readerIndex(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void readerIndexBoundaryCheck2() { + try { + buffer.writerIndex(buffer.capacity()); + } catch (IndexOutOfBoundsException e) { + fail(); + } + buffer.readerIndex(buffer.capacity() + 1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void readerIndexBoundaryCheck3() { + try { + buffer.writerIndex(CAPACITY / 2); + } catch (IndexOutOfBoundsException e) { + fail(); + } + buffer.readerIndex(CAPACITY * 3 / 2); + } + + @Test + public void readerIndexBoundaryCheck4() { + buffer.writerIndex(0); + buffer.readerIndex(0); + buffer.writerIndex(buffer.capacity()); + buffer.readerIndex(buffer.capacity()); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void writerIndexBoundaryCheck1() { + buffer.writerIndex(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void writerIndexBoundaryCheck2() { + try { + buffer.writerIndex(CAPACITY); + buffer.readerIndex(CAPACITY); + } catch (IndexOutOfBoundsException e) { + fail(); + } + buffer.writerIndex(buffer.capacity() + 1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void writerIndexBoundaryCheck3() { + try { + buffer.writerIndex(CAPACITY); + buffer.readerIndex(CAPACITY / 2); + } catch (IndexOutOfBoundsException e) { + fail(); + } + buffer.writerIndex(CAPACITY / 4); + } + + @Test + public void writerIndexBoundaryCheck4() { + buffer.writerIndex(0); + buffer.readerIndex(0); + buffer.writerIndex(CAPACITY); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getByteBoundaryCheck1() { + buffer.getByte(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getByteBoundaryCheck2() { + buffer.getByte(buffer.capacity()); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getShortBoundaryCheck1() { + buffer.getShort(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getShortBoundaryCheck2() { + buffer.getShort(buffer.capacity() - 1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getMediumBoundaryCheck1() { + buffer.getMedium(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getMediumBoundaryCheck2() { + buffer.getMedium(buffer.capacity() - 2); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getIntBoundaryCheck1() { + buffer.getInt(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getIntBoundaryCheck2() { + buffer.getInt(buffer.capacity() - 3); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getLongBoundaryCheck1() { + buffer.getLong(-1); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getLongBoundaryCheck2() { + buffer.getLong(buffer.capacity() - 7); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getByteArrayBoundaryCheck1() { + buffer.getBytes(-1, new byte[0]); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getByteArrayBoundaryCheck2() { + buffer.getBytes(-1, new byte[0], 0, 0); + } + + public void getByteArrayBoundaryCheck3() { + byte[] dst = new byte[4]; + buffer.setInt(0, 0x01020304); + try { + buffer.getBytes(0, dst, -1, 4); + fail(); + } catch (IndexOutOfBoundsException e) { + // Success + } + + // No partial copy is expected. + assertEquals(0, dst[0]); + assertEquals(0, dst[1]); + assertEquals(0, dst[2]); + assertEquals(0, dst[3]); + } + + public void getByteArrayBoundaryCheck4() { + byte[] dst = new byte[4]; + buffer.setInt(0, 0x01020304); + try { + buffer.getBytes(0, dst, 1, 4); + fail(); + } catch (IndexOutOfBoundsException e) { + // Success + } + + // No partial copy is expected. + assertEquals(0, dst[0]); + assertEquals(0, dst[1]); + assertEquals(0, dst[2]); + assertEquals(0, dst[3]); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getByteBufferBoundaryCheck() { + buffer.getBytes(-1, ByteBuffer.allocate(0)); + } + + public void getByteBufferState1() { + ByteBuffer dst = ByteBuffer.allocate(8); + dst.position(1); + + buffer.setInt(0, 0x01020304); + buffer.getBytes(0, dst); + + assertEquals(0x0001020304000000L, dst.getLong(0)); + assertEquals(5, dst.position()); + assertEquals(8, dst.limit()); + } + + public void getByteBufferState2() { + ByteBuffer dst = ByteBuffer.allocate(4); + dst.position(1); + dst.limit(3); + + buffer.setInt(0, 0x01020304); + buffer.getBytes(1, dst); + assertEquals(0x00020300, dst.getInt(0)); + assertEquals(3, dst.position()); + assertEquals(3, dst.limit()); + } + + @Test(expected=IndexOutOfBoundsException.class) + public void getDirectByteBufferBoundaryCheck() { + buffer.getBytes(-1, ByteBuffer.allocateDirect(0)); + } + + public void getDirectByteBufferState1() { + ByteBuffer dst = ByteBuffer.allocateDirect(8); + dst.position(1); + + buffer.setInt(0, 0x01020304); + buffer.getBytes(0, dst); + + assertEquals(0x0001020304000000L, dst.getLong(0)); + assertEquals(5, dst.position()); + assertEquals(8, dst.limit()); + } + + public void getDirectByteBufferState2() { + ByteBuffer dst = ByteBuffer.allocateDirect(4); + dst.position(1); + dst.limit(3); + + buffer.setInt(0, 0x01020304); + buffer.getBytes(1, dst); + assertEquals(0x00020300, dst.getInt(0)); + assertEquals(3, dst.position()); + assertEquals(3, dst.limit()); + } + + @Test + public void testRandomByteAccess() { + for (int i = 0; i < buffer.capacity(); i ++) { + byte value = (byte) random.nextInt(); + buffer.setByte(i, value); + } + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity(); i ++) { + byte value = (byte) random.nextInt(); + assertEquals(value, buffer.getByte(i)); + } + } + + @Test + public void testRandomShortAccess() { + for (int i = 0; i < buffer.capacity() - 1; i += 2) { + short value = (short) random.nextInt(); + buffer.setShort(i, value); + } + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity() - 1; i += 2) { + short value = (short) random.nextInt(); + assertEquals(value, buffer.getShort(i)); + } + } + + @Test + public void testRandomMediumAccess() { + for (int i = 0; i < buffer.capacity() - 2; i += 3) { + int value = random.nextInt(); + buffer.setMedium(i, value); + } + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity() - 2; i += 3) { + int value = random.nextInt() & 0x00FFFFFF; + assertEquals(value, buffer.getMedium(i)); + } + } + + @Test + public void testRandomIntAccess() { + for (int i = 0; i < buffer.capacity() - 3; i += 4) { + int value = random.nextInt(); + buffer.setInt(i, value); + } + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity() - 3; i += 4) { + int value = random.nextInt(); + assertEquals(value, buffer.getInt(i)); + } + } + + @Test + public void testRandomLongAccess() { + for (int i = 0; i < buffer.capacity() - 7; i += 8) { + long value = random.nextLong(); + buffer.setLong(i, value); + } + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity() - 7; i += 8) { + long value = random.nextLong(); + assertEquals(value, buffer.getLong(i)); + } + } + + @Test + public void testSequentialByteAccess() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity(); i ++) { + byte value = (byte) random.nextInt(); + assertEquals(i, buffer.writerIndex()); + assertTrue(buffer.writable()); + buffer.writeByte(value); + } + + assertEquals(0, buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.writable()); + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity(); i ++) { + byte value = (byte) random.nextInt(); + assertEquals(i, buffer.readerIndex()); + assertTrue(buffer.readable()); + assertEquals(value, buffer.readByte()); + } + + assertEquals(buffer.capacity(), buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.readable()); + assertFalse(buffer.writable()); + } + + @Test + public void testSequentialShortAccess() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity(); i += 2) { + short value = (short) random.nextInt(); + assertEquals(i, buffer.writerIndex()); + assertTrue(buffer.writable()); + buffer.writeShort(value); + } + + assertEquals(0, buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.writable()); + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity(); i += 2) { + short value = (short) random.nextInt(); + assertEquals(i, buffer.readerIndex()); + assertTrue(buffer.readable()); + assertEquals(value, buffer.readShort()); + } + + assertEquals(buffer.capacity(), buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.readable()); + assertFalse(buffer.writable()); + } + + @Test + public void testSequentialMediumAccess() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity() / 3 * 3; i += 3) { + int value = random.nextInt() & 0x00FFFFFF; + assertEquals(i, buffer.writerIndex()); + assertTrue(buffer.writable()); + buffer.writeMedium(value); + } + + assertEquals(0, buffer.readerIndex()); + assertEquals(buffer.capacity() / 3 * 3, buffer.writerIndex()); + assertEquals(buffer.capacity() % 3, buffer.writableBytes()); + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity() / 3 * 3; i += 3) { + int value = random.nextInt() & 0x00FFFFFF; + assertEquals(i, buffer.readerIndex()); + assertTrue(buffer.readable()); + assertEquals(value, buffer.readMedium()); + } + + assertEquals(buffer.capacity() / 3 * 3, buffer.readerIndex()); + assertEquals(buffer.capacity() / 3 * 3, buffer.writerIndex()); + assertEquals(0, buffer.readableBytes()); + assertEquals(buffer.capacity() % 3, buffer.writableBytes()); + } + + @Test + public void testSequentialIntAccess() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity(); i += 4) { + int value = random.nextInt(); + assertEquals(i, buffer.writerIndex()); + assertTrue(buffer.writable()); + buffer.writeInt(value); + } + + assertEquals(0, buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.writable()); + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity(); i += 4) { + int value = random.nextInt(); + assertEquals(i, buffer.readerIndex()); + assertTrue(buffer.readable()); + assertEquals(value, buffer.readInt()); + } + + assertEquals(buffer.capacity(), buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.readable()); + assertFalse(buffer.writable()); + } + + @Test + public void testSequentialLongAccess() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity(); i += 8) { + long value = random.nextLong(); + assertEquals(i, buffer.writerIndex()); + assertTrue(buffer.writable()); + buffer.writeLong(value); + } + + assertEquals(0, buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.writable()); + + random.setSeed(seed); + for (int i = 0; i < buffer.capacity(); i += 8) { + long value = random.nextLong(); + assertEquals(i, buffer.readerIndex()); + assertTrue(buffer.readable()); + assertEquals(value, buffer.readLong()); + } + + assertEquals(buffer.capacity(), buffer.readerIndex()); + assertEquals(buffer.capacity(), buffer.writerIndex()); + assertFalse(buffer.readable()); + assertFalse(buffer.writable()); + } + + @Test + public void testByteArrayTransfer() { + byte[] value = new byte[BLOCK_SIZE * 2]; + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(value); + buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); + } + + random.setSeed(seed); + byte[] expectedValue = new byte[BLOCK_SIZE * 2]; + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValue); + int valueOffset = random.nextInt(BLOCK_SIZE); + buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue[j], value[j]); + } + } + } + + @Test + public void testRandomHeapBufferTransfer() { + byte[] valueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = wrappedBuffer(valueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(valueContent); + buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); + } + + random.setSeed(seed); + byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValueContent); + int valueOffset = random.nextInt(BLOCK_SIZE); + buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + } + } + + @Test + public void testRandomDirectBufferTransfer() { + byte[] tmp = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(tmp); + value.setBytes(0, tmp, 0, value.capacity()); + buffer.setBytes(i, value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); + } + + random.setSeed(seed); + ChannelBuffer expectedValue = directBuffer(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(tmp); + expectedValue.setBytes(0, tmp, 0, expectedValue.capacity()); + int valueOffset = random.nextInt(BLOCK_SIZE); + buffer.getBytes(i, value, valueOffset, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + } + } + + @Test + public void testRandomByteBufferTransfer() { + ByteBuffer value = ByteBuffer.allocate(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(value.array()); + value.clear().position(random.nextInt(BLOCK_SIZE)); + value.limit(value.position() + BLOCK_SIZE); + buffer.setBytes(i, value); + } + + random.setSeed(seed); + ByteBuffer expectedValue = ByteBuffer.allocate(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValue.array()); + int valueOffset = random.nextInt(BLOCK_SIZE); + value.clear().position(valueOffset).limit(valueOffset + BLOCK_SIZE); + buffer.getBytes(i, value); + assertEquals(valueOffset + BLOCK_SIZE, value.position()); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.get(j), value.get(j)); + } + } + } + + @Test + public void testSequentialHeapBufferTransfer1() { + byte[] valueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = wrappedBuffer(valueContent); + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(valueContent); + assertEquals(0, buffer.readerIndex()); + assertEquals(i, buffer.writerIndex()); + buffer.writeBytes(value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); + assertEquals(0, value.readerIndex()); + assertEquals(valueContent.length, value.writerIndex()); + } + + random.setSeed(seed); + byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValueContent); + int valueOffset = random.nextInt(BLOCK_SIZE); + assertEquals(i, buffer.readerIndex()); + assertEquals(CAPACITY, buffer.writerIndex()); + buffer.readBytes(value, valueOffset, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + assertEquals(0, value.readerIndex()); + assertEquals(valueContent.length, value.writerIndex()); + } + } + + @Test + public void testSequentialHeapBufferTransfer2() { + byte[] valueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = wrappedBuffer(valueContent); + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(valueContent); + assertEquals(0, buffer.readerIndex()); + assertEquals(i, buffer.writerIndex()); + int readerIndex = random.nextInt(BLOCK_SIZE); + value.readerIndex(readerIndex); + value.writerIndex(readerIndex + BLOCK_SIZE); + buffer.writeBytes(value); + assertEquals(readerIndex + BLOCK_SIZE, value.writerIndex()); + assertEquals(value.writerIndex(), value.readerIndex()); + } + + random.setSeed(seed); + byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValueContent); + int valueOffset = random.nextInt(BLOCK_SIZE); + assertEquals(i, buffer.readerIndex()); + assertEquals(CAPACITY, buffer.writerIndex()); + value.readerIndex(valueOffset); + value.writerIndex(valueOffset); + buffer.readBytes(value, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + assertEquals(valueOffset, value.readerIndex()); + assertEquals(valueOffset + BLOCK_SIZE, value.writerIndex()); + } + } + + @Test + public void testSequentialDirectBufferTransfer1() { + byte[] valueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(valueContent); + value.setBytes(0, valueContent); + assertEquals(0, buffer.readerIndex()); + assertEquals(i, buffer.writerIndex()); + buffer.writeBytes(value, random.nextInt(BLOCK_SIZE), BLOCK_SIZE); + assertEquals(0, value.readerIndex()); + assertEquals(0, value.writerIndex()); + } + + random.setSeed(seed); + byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValueContent); + int valueOffset = random.nextInt(BLOCK_SIZE); + value.setBytes(0, valueContent); + assertEquals(i, buffer.readerIndex()); + assertEquals(CAPACITY, buffer.writerIndex()); + buffer.readBytes(value, valueOffset, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + assertEquals(0, value.readerIndex()); + assertEquals(0, value.writerIndex()); + } + } + + @Test + public void testSequentialDirectBufferTransfer2() { + byte[] valueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer value = directBuffer(BLOCK_SIZE * 2); + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(valueContent); + value.setBytes(0, valueContent); + assertEquals(0, buffer.readerIndex()); + assertEquals(i, buffer.writerIndex()); + int readerIndex = random.nextInt(BLOCK_SIZE); + value.readerIndex(0); + value.writerIndex(readerIndex + BLOCK_SIZE); + value.readerIndex(readerIndex); + buffer.writeBytes(value); + assertEquals(readerIndex + BLOCK_SIZE, value.writerIndex()); + assertEquals(value.writerIndex(), value.readerIndex()); + } + + random.setSeed(seed); + byte[] expectedValueContent = new byte[BLOCK_SIZE * 2]; + ChannelBuffer expectedValue = wrappedBuffer(expectedValueContent); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValueContent); + value.setBytes(0, valueContent); + int valueOffset = random.nextInt(BLOCK_SIZE); + assertEquals(i, buffer.readerIndex()); + assertEquals(CAPACITY, buffer.writerIndex()); + value.readerIndex(valueOffset); + value.writerIndex(valueOffset); + buffer.readBytes(value, BLOCK_SIZE); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.getByte(j), value.getByte(j)); + } + assertEquals(valueOffset, value.readerIndex()); + assertEquals(valueOffset + BLOCK_SIZE, value.writerIndex()); + } + } + + @Test + public void testSequentialByteBufferTransfer() { + buffer.writerIndex(0); + ByteBuffer value = ByteBuffer.allocate(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(value.array()); + value.clear().position(random.nextInt(BLOCK_SIZE)); + value.limit(value.position() + BLOCK_SIZE); + buffer.writeBytes(value); + } + + random.setSeed(seed); + ByteBuffer expectedValue = ByteBuffer.allocate(BLOCK_SIZE * 2); + for (int i = 0; i < buffer.capacity() - BLOCK_SIZE + 1; i += BLOCK_SIZE) { + random.nextBytes(expectedValue.array()); + int valueOffset = random.nextInt(BLOCK_SIZE); + value.clear().position(valueOffset).limit(valueOffset + BLOCK_SIZE); + buffer.readBytes(value); + assertEquals(valueOffset + BLOCK_SIZE, value.position()); + for (int j = valueOffset; j < valueOffset + BLOCK_SIZE; j ++) { + assertEquals(expectedValue.get(j), value.get(j)); + } + } + } + + @Test + public void testDiscardReadBytes() { + buffer.writerIndex(0); + for (int i = 0; i < buffer.capacity(); i += 4) { + buffer.writeInt(i); + } + ChannelBuffer copy = copiedBuffer(buffer); + + // Make sure there's no effect if called when readerIndex is 0. + buffer.readerIndex(CAPACITY / 4); + buffer.markReaderIndex(); + buffer.writerIndex(CAPACITY / 3); + buffer.markWriterIndex(); + buffer.readerIndex(0); + buffer.writerIndex(CAPACITY / 2); + buffer.discardReadBytes(); + + assertEquals(0, buffer.readerIndex()); + assertEquals(CAPACITY / 2, buffer.writerIndex()); + assertEquals(copy.slice(0, CAPACITY / 2), buffer.slice(0, CAPACITY / 2)); + buffer.resetReaderIndex(); + assertEquals(CAPACITY / 4, buffer.readerIndex()); + buffer.resetWriterIndex(); + assertEquals(CAPACITY / 3, buffer.writerIndex()); + + // Make sure bytes after writerIndex is not copied. + buffer.readerIndex(1); + buffer.writerIndex(CAPACITY / 2); + buffer.discardReadBytes(); + + assertEquals(0, buffer.readerIndex()); + assertEquals(CAPACITY / 2 - 1, buffer.writerIndex()); + assertEquals(copy.slice(1, CAPACITY / 2 - 1), buffer.slice(0, CAPACITY / 2 - 1)); + assertEquals(copy.slice(CAPACITY / 2, CAPACITY / 2), buffer.slice(CAPACITY / 2, CAPACITY / 2)); + + // Marks also should be relocated. + buffer.resetReaderIndex(); + assertEquals(CAPACITY / 4 - 1, buffer.readerIndex()); + buffer.resetWriterIndex(); + assertEquals(CAPACITY / 3 - 1, buffer.writerIndex()); + } + + @Test + public void testCopy() { + for (int i = 0; i < buffer.capacity(); i ++) { + byte value = (byte) random.nextInt(); + buffer.setByte(i, value); + } + + final int readerIndex = CAPACITY / 3; + final int writerIndex = CAPACITY * 2 / 3; + buffer.setIndex(readerIndex, writerIndex); + + // Make sure all properties are cpoied. + ChannelBuffer copy = buffer.copy(); + assertEquals(0, copy.readerIndex()); + assertEquals(buffer.readableBytes(), copy.writerIndex()); + assertEquals(copy.capacity(), buffer.readableBytes()); + for (int i = 0; i < copy.capacity(); i ++) { + assertEquals(buffer.getByte(i + readerIndex), copy.getByte(i)); + } + + // Make sure the buffer contents are independent from each other. + buffer.setByte(readerIndex, (byte) (buffer.getByte(readerIndex) + 1)); + assertTrue(buffer.getByte(readerIndex) != copy.getByte(0)); + copy.setByte(1, (byte) (copy.getByte(1) + 1)); + assertTrue(buffer.getByte(readerIndex + 1) != copy.getByte(1)); + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBufferTest.java new file mode 100644 index 000000000000..186d1378f466 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/BigEndianHeapChannelBufferTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + + + + +public class BigEndianHeapChannelBufferTest extends AbstractChannelBufferTest { + + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.buffer(length); + assertEquals(0, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/ChannelBuffersTest.java b/src/test/java/net/gleamynode/netty/buffer/ChannelBuffersTest.java new file mode 100644 index 000000000000..b10e3c55a0d1 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/ChannelBuffersTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Test; + +/** + * @author The Netty Project (netty@googlegroups.com) + * @author Trustin Lee (trustin@gmail.com) + * + * @version $Rev$, $Date$ + * + */ +public class ChannelBuffersTest { + + @Test + public void testCompositeWrappedBuffer() { + ChannelBuffer header = ChannelBuffers.dynamicBuffer(12); + ChannelBuffer payload = ChannelBuffers.dynamicBuffer(512); + + header.writeBytes(new byte[12]); + payload.writeBytes(new byte[512]); + + ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(header, payload); + + assertTrue(header.readableBytes() == 12); + assertTrue(payload.readableBytes() == 512); + + assertEquals(12 + 512, buffer.readableBytes()); + + assertEquals(12 + 512, buffer.toByteBuffer(0, 12 + 512).remaining()); + } + + @Test + public void testHashCode() { + Map map = new LinkedHashMap(); + map.put(new byte[0], 1); + map.put(new byte[] { 1 }, 32); + map.put(new byte[] { 2 }, 33); + map.put(new byte[] { 0, 1 }, 962); + map.put(new byte[] { 1, 2 }, 994); + map.put(new byte[] { 0, 1, 2, 3, 4, 5 }, 63504931); + map.put(new byte[] { 6, 7, 8, 9, 0, 1 }, (int) 97180294697L); + map.put(new byte[] { -1, -1, -1, (byte) 0xE1 }, 1); + + for (Entry e: map.entrySet()) { + assertEquals( + e.getValue().intValue(), + ChannelBuffers.hashCode(ChannelBuffers.wrappedBuffer(e.getKey()))); + } + } + + @Test + public void testEquals() { + ChannelBuffer a, b; + + // Different length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2 }); + assertFalse(ChannelBuffers.equals(a, b)); + + // Same content, same firstIndex, short length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3 }); + assertTrue(ChannelBuffers.equals(a, b)); + + // Same content, different firstIndex, short length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 3); + assertTrue(ChannelBuffers.equals(a, b)); + + // Different content, same firstIndex, short length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 4 }); + assertFalse(ChannelBuffers.equals(a, b)); + + // Different content, different firstIndex, short length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 3); + assertFalse(ChannelBuffers.equals(a, b)); + + // Same content, same firstIndex, long length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + assertTrue(ChannelBuffers.equals(a, b)); + + // Same content, different firstIndex, long length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 1, 10); + assertTrue(ChannelBuffers.equals(a, b)); + + // Different content, same firstIndex, long length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 6, 7, 8, 5, 9, 10 }); + assertFalse(ChannelBuffers.equals(a, b)); + + // Different content, different firstIndex, long length. + a = ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + b = ChannelBuffers.wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 1, 10); + assertFalse(ChannelBuffers.equals(a, b)); + } + + @Test + public void testCompare() { + List expected = new ArrayList(); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 })); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4 }, 1, 1)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4 }, 2, 2)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 1, 10)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 2, 12)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4, 5 }, 2, 1)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5 }, 3, 2)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 2, 10)); + expected.add(ChannelBuffers.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 3, 12)); + + for (int i = 0; i < expected.size(); i ++) { + for (int j = 0; j < expected.size(); j ++) { + if (i == j) { + assertEquals(0, ChannelBuffers.compare(expected.get(i), expected.get(j))); + } else if (i < j) { + assertTrue(ChannelBuffers.compare(expected.get(i), expected.get(j)) < 0); + } else { + assertTrue(ChannelBuffers.compare(expected.get(i), expected.get(j)) > 0); + } + } + } + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/CompositeChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/CompositeChannelBufferTest.java new file mode 100644 index 000000000000..6d4b7b3704ec --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/CompositeChannelBufferTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + + + + +public class CompositeChannelBufferTest extends AbstractChannelBufferTest { + + private List buffers; + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffers = new ArrayList(); + for (int i = 0; i < length; i += 10) { + buffers.add(ChannelBuffers.wrappedBuffer(new byte[1])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[2])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[3])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[4])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[5])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[6])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[7])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[8])); + buffers.add(ChannelBuffers.wrappedBuffer(new byte[9])); + } + + buffer = ChannelBuffers.wrappedBuffer(buffers.toArray(new ChannelBuffer[buffers.size()])); + buffer.writerIndex(length); + buffer = ChannelBuffers.wrappedBuffer(buffer); + assertEquals(length, buffer.capacity()); + assertEquals(length, buffer.readableBytes()); + assertFalse(buffer.writable()); + buffer.writerIndex(0); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return buffers.toArray(new ChannelBuffer[buffers.size()]); + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/DirectChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/DirectChannelBufferTest.java new file mode 100644 index 000000000000..4ec68e32d984 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/DirectChannelBufferTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + + + + +public class DirectChannelBufferTest extends AbstractChannelBufferTest { + + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.directBuffer(length); + assertEquals(0, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/DuplicateChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/DuplicateChannelBufferTest.java new file mode 100644 index 000000000000..5b7687f96d10 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/DuplicateChannelBufferTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + + + + +public class DuplicateChannelBufferTest extends AbstractChannelBufferTest { + + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.buffer(length).duplicate(); + assertEquals(0, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBufferTest.java new file mode 100644 index 000000000000..d1d30a69aad0 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/LittleEndianHeapChannelBufferTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + +import java.nio.ByteOrder; + +public class LittleEndianHeapChannelBufferTest extends AbstractChannelBufferTest { + + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, length); + assertEquals(0, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/SlicedChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/SlicedChannelBufferTest.java new file mode 100644 index 000000000000..ad39c1e6eb93 --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/SlicedChannelBufferTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + +import java.util.Random; + + +public class SlicedChannelBufferTest extends AbstractChannelBufferTest { + + private final Random random = new Random(); + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.wrappedBuffer( + new byte[length * 2], random.nextInt(length - 1) + 1, length); + assertEquals(length, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +} diff --git a/src/test/java/net/gleamynode/netty/buffer/TruncatedChannelBufferTest.java b/src/test/java/net/gleamynode/netty/buffer/TruncatedChannelBufferTest.java new file mode 100644 index 000000000000..ec2d6f6e8bfd --- /dev/null +++ b/src/test/java/net/gleamynode/netty/buffer/TruncatedChannelBufferTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Trustin Heuiseung Lee + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA + */ +package net.gleamynode.netty.buffer; + +import static org.junit.Assert.*; + + + + +public class TruncatedChannelBufferTest extends AbstractChannelBufferTest { + + private ChannelBuffer buffer; + + @Override + protected ChannelBuffer newBuffer(int length) { + buffer = ChannelBuffers.wrappedBuffer( + new byte[length * 2], 0, length); + assertEquals(length, buffer.writerIndex()); + return buffer; + } + + @Override + protected ChannelBuffer[] components() { + return new ChannelBuffer[] { buffer }; + } +}