class RJR::Nodes::Missing

Web node definition, listen for and invoke json-rpc requests via web requests

Clients should specify the hostname / port when listening for requests and when invoking them.

note the RJR javascript client also supports sending / receiving json-rpc messages over http

@example Listening for json-rpc requests over tcp

# initialize node
server = RJR::Nodes::Web.new :node_id => 'server', :host => 'localhost', :port => '7777'

# register rjr dispatchers (see RJR::Dispatcher)
server.dispatcher.handle('hello') do |name|
  "Hello #{name}!"
end

# listen, and block
server.listen
server.join

@example Invoking json-rpc requests over http using rjr

client = RJR::Nodes::Web.new :node_id => 'client'
puts client.invoke('http://localhost:7777', 'hello', 'mo')

@example Invoking json-rpc requests over http using curl

sh> curl -X POST http://localhost:7777 -d '{"jsonrpc":"2.0","method":"hello","params":["mo"],"id":"123"}'
  > {"jsonrpc":"2.0","id":"123","result":"Hello mo!"}

Web socket node definition, listen for and invoke json-rpc requests via web sockets

Clients should specify the hostname / port when listening for and invoking requests.

note the RJR javascript client also supports sending / receiving json-rpc messages over web sockets

@example Listening for json-rpc requests over tcp

# initialize node
server = RJR::Nodes::WS.new :node_id => 'server', :host => 'localhost', :port => '7777'

# register rjr dispatchers (see RJR::Dispatcher)
server.dispatcher.handle('hello') do |name|
  "Hello #{name}!"
end

# listen, and block
server.listen
server.join

@example Invoking json-rpc requests over web sockets using rjr

client = RJR::Nodes::WS.new :node_id => 'client'
puts client.invoke_request('ws://localhost:7777', 'hello', 'mo')

Constants

INDIRECT_NODE
PERSISTENT_NODE
RJR_NODE_TYPE

Public Class Methods

new(args = {}) click to toggle source

Web initializer @param [Hash] args the options to create the tcp node with @option args [String] :host the hostname/ip which to listen on @option args [Integer] :port the port which to listen on

Calls superclass method RJR::Node::new
# File lib/rjr/nodes/web.rb, line 106
def initialize(args = {})
   super(args)
   @host      = args[:host]
   @port      = args[:port]
end

Public Instance Methods

invoke(uri, rpc_method, *args) click to toggle source

Instructs node to send rpc request, and wait for / return response

Implementation of RJR::Node#invoke

Do not invoke directly from em event loop or callback as will block the message subscription used to receive responses

@param [String] uri location of node to send request to, should be

in format of http://hostname:port

@param [String] rpc_method json-rpc method to invoke on destination @param [Array] args array of arguments to convert to json and invoke remote method wtih

# File lib/rjr/nodes/web.rb, line 155
def invoke(uri, rpc_method, *args)
  message = Messages::Request.new :method => rpc_method,
                                  :args   => args,
                                  :headers => @message_headers
  cb = lambda { |http|
    # TODO handle errors
    handle_message(http.response, http)
  }

  @@em.schedule do
    http = EventMachine::HttpRequest.new(uri).post :body => message.to_s,
                                                   :head => {'content-type' => 'application/json'}
    http.errback  &cb
    http.callback &cb
  end

  # will block until response message is received
  # TODO optional timeout for response ?
  result = wait_for_result(message)
  if result.size > 2
    fail result[2]
  end
  return result[1]
end
listen() click to toggle source

Instruct Node to start listening for and dispatching rpc requests

Implementation of RJR::Node#listen

# File lib/rjr/nodes/web.rb, line 137
def listen
  @@em.schedule do
    EventMachine::start_server(@host, @port, WebConnection, :rjr_node => self)
  end
  self
end
method_missing(method_id, *args, &bl) click to toggle source
# File lib/rjr/nodes/missing.rb, line 14
def method_missing(method_id, *args, &bl)
  raise "rjr node #{node_id} is missing a dependency - cannot invoke #{method_id}"
end
notify(uri, rpc_method, *args) click to toggle source

Instructs node to send rpc notification (immadiately returns / no response is generated)

Implementation of RJR::Node#notify

@param [String] uri location of node to send request to, should be

in format of http://hostname:port

@param [String] rpc_method json-rpc method to invoke on destination @param [Array] args array of arguments to convert to json and invoke remote method wtih

# File lib/rjr/nodes/web.rb, line 188
def notify(uri, rpc_method, *args)
  # will block until message is published
  published_l = Mutex.new
  published_c = ConditionVariable.new

  invoked = false
  message = Messages::Notification.new :method => rpc_method,
                                       :args   => args,
                                       :headers => @message_headers
  cb = lambda { |arg| published_l.synchronize { invoked = true ; published_c.signal }}
  @@em.schedule do
    http = EventMachine::HttpRequest.new(uri).post :body => message.to_s,
                                                   :head => {'content-type' => 'application/json'}
    http.errback  &cb
    http.callback &cb
  end
  published_l.synchronize { published_c.wait published_l unless invoked }
  nil
end
send_msg(data, connection) click to toggle source

Send data using specified http connection

Implementation of RJR::Node#send_msg

# File lib/rjr/nodes/web.rb, line 119
def send_msg(data, connection)
  # we are assuming that since http connections
  # are not persistant, we should be sending a
  # response message here

  @@em.schedule  do
    resp = EventMachine::DelegatedHttpResponse.new(connection)
    #resp.status  = response.result.success ? 200 : 500
    resp.status = 200
    resp.content = data.to_s
    resp.content_type "application/json"
    resp.send_response
  end
end
to_s() click to toggle source
# File lib/rjr/nodes/web.rb, line 112
def to_s
  "RJR::Nodes::Web<#{@node_id},#{@host},#{@port}>"
end

Private Instance Methods

init_client(uri, &on_init) click to toggle source

Internal helper initialize new client

# File lib/rjr/nodes/ws.rb, line 63
def init_client(uri, &on_init)
  connection = nil
  @connections_lock.synchronize {
    connection = @connections.find { |c|
                   c.url == uri
                 }
    if connection.nil?
      connection = EventMachine::WebSocketClient.connect(uri)
      connection.callback do
        on_init.call(connection)
      end
      @connections << connection
      # TODO sleep until connected?
    else
      on_init.call(connection)
    end
  }
  connection
end