Skip to content

Instantly share code, notes, and snippets.

@mikewadhera
Created December 14, 2009 03:59
Show Gist options
  • Save mikewadhera/255763 to your computer and use it in GitHub Desktop.
Save mikewadhera/255763 to your computer and use it in GitHub Desktop.
ruby on netty
# Purely experimental exercise in exploring ideas for a JRuby gem for Netty -- http://jboss.org/netty
# Based on TimeClient example in Netty docs
require "java"
require "netty"
require "unix_time"
TimeClient = TcpNioClient.new do |pipeline|
pipeline.insert :frame_decoder do
def decode(context, channel, buffer)
UnixTime.new(buffer.read_integer) unless buffer.readable_bytes < 4
end
end
pipeline.insert :handler do
on_new_message do |context, event|
puts event.message.inspect
event.channel.close
end
end
end
TimeClient.connect :host => "localhost", :port => 8080
# Purely experimental exercise in exploring ideas for a JRuby gem for Netty -- http://jboss.org/netty
# Based on TimeServer example in Netty docs
require "java"
require "netty"
require "unix_time"
# A custom Handler that extends ChannelHandlers::Simple a SimpleChannelHandler
# Tries to separate the handler's lifecycle from the independent handler actions
class TimeServerHandler < ChannelHandlers::Simple
on_connect :write_unix_time
on_open :add_to_active_channels
def write_unix_time(context, event)
time = UnixTime.new(java.lang.System.current_time_millis / 1000)
write = event.channel.write(time)
write.add_listener(:close) # sugar for add_listener(Channel::ChannelFutureListener::CLOSE)
end
# Maybe push this into Base, if possible -- could also capture other common actions
def add_to_active_channels(context, event)
TimeServer.active_channels << event.channel
end
# Hook for exception handling. Base implementation prints stack trace on standard error & closes channel
def rescue_exception(exception, channel)
MyLogger.error(exception)
super
end
end
# DSL for defining a pipeline factory, inspired by Rails routes.rb
pipeline_factory = PipelineFactory.assemble do |pipeline|
# Alternate #insert forms:
# pipeline.insert TimeEncoder.new -- infers name from class
pipeline.insert :encoder do
on_write_request do |context, event|
buffer = Buffer.new(4).write_integer(event.message) # sugar for write_int(arg.to_i)
write(context, event.future, buffer) # what about doing this automatically if the method/block returns a Buffer?
end
end
pipeline.insert :handler, TimeServerHandler.new
end
# Server class bootstrapping common combos of transport/IO Model, handling shutdown & active channels, joining event loop, etc.
# One could imagine similar classes for client side
# Alternate #initialize forms:
# TcpNioServer.new(pipeline_factory, :port => 8080, :threads => 5, :shutdown => ["TERM"]) -- uses separately defined PipelineFactory above
TimeServer = TcpNioServer.new :port => 8080 do |pipeline| # this form yields a PipelineFactory for inline assembly
pipeline.insert :encoder do
on_write_request do |context, event|
buffer = Buffer.new(4).write_integer(event.message) # sugar for write_int(arg.to_i)
write(context, event.future, buffer) # what about doing this automatically if the method/block returns a Buffer?
end
end
pipeline.insert :handler, TimeServerHandler.new
end
puts "TimeServer running on #{TimeServer.port}"
TimeServer.join # just future.await_uninterruptibly
class UnixTime
attr_reader :value
def initialize(value)
@value = value
end
def to_i
@value
end
def inspect
java.util.Date.new(@value * 1000).to_string
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment