Re: How to keep persistent socket connections?

Steve Hull
Fri, 22 May 2009 00:05:45 -0000

Roy --

Thank you for that example!!  I guess I''ll give that a shot    :)

It still hurts my heart that this doesn''t appear to be possible with
Rails.  :(

One followup question, if I may:
Why is it that class variables don''t behave as I expect them to?  So
since it appears that adding class attributes (inheritable or not) to a
descendent of ActiveRecord is unsupported, I added a new Class
(inheriting from Object) and put it in /lib:

class ConnectionManager
  HOST = "the.host.com"
  PATH = ''/''
  PORT = 2195

  PASSPHRASE = "foobar"
  CACERT = File.expand_path(File.dirname(__FILE__) +
"certs/ca.the.host.com.crt")
  USERAGENT = ''Mozilla/5.0 (ConnectionManager Ruby on Rails
0.1)''
  cattr_accessor :connection, :cert_name, :cert
  self.cert_name = "my_pem_file.pem"

  def initialize
    super
    cert = File.read("config/#{cert_name}") if
File.exists?("config/#{cert_name}")
    puts "cert = #{cert.inspect}"
    if connection.nil?
      puts "Connecting now!!"
      ctx = OpenSSL::SSL::SSLContext.new
      ctx.key = OpenSSL::PKey::RSA.new(cert, PASSPHRASE)
      ctx.cert = OpenSSL::X509::Certificate.new(cert)


      s = TCPSocket.new(HOST, PORT)
      connection = OpenSSL::SSL::SSLSocket.new(s, ctx)
      connection.sync = true
      connection.connect
    end
  end
end

But when I try to use this in my class that inherits from ActiveRecord,
like this:

  def send
    ssl = cm.class.connection
    logger.info "ssl = #{ssl.inspect}"
    ssl.write(self.message_for_sending)

  rescue SocketError => error
    raise "Error while sending: #{error}"
  end

First I got an error and I realized that my ConnectionManager''s
initialize method wasn''t being called.  So I added a line to get an
instance of ConnectionManager, just so that initialize would be called:

  def send_notification
    cm = ConnectionManager.new
    ssl = cm.class.connection
    logger.info "ssl = #{ssl.inspect}"
    ssl.write(self.message_for_sending)

  rescue SocketError => error
    raise "Error while sending notifications: #{error}"
  end

I could tell from my log ("Connecting Now!!" appeared) that initialize
had been called.  But I''m still getting an error:

You have a nil object when you didn''t expect it! The error occurred
while evaluating nil.write

Clearly ConnectionManager.connection is returning null.  In Java, once
you''ve used a class, its static vars are instantiated and hold their
values.  It seems that in Ruby, the class''s static vars are
instantiated/sandboxed in each .rb file.  Am I correct here, or no?

Thanks again for the example code Roy!

-Steve