stomp_message 0.0.3 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt CHANGED
@@ -1,2 +1,8 @@
1
+ 0.0.8
2
+ fix for not closing topics
3
+
4
+
1
5
  Initial release of stomp message..
2
- bulk casing for stomp messages
6
+ xml casing for stomp messages
7
+
8
+ arbirtrary sending of stomp messages to any topic
data/Manifest.txt CHANGED
@@ -7,6 +7,10 @@ config/hoe.rb
7
7
  config/requirements.rb
8
8
  lib/stomp_message.rb
9
9
  lib/stomp_message/version.rb
10
+ lib/stomp_message/stomp_server.rb
11
+ lib/stomp_message/stomp_statistics_server.rb
12
+ lib/stomp_message/stomp_send_topic.rb
13
+ lib/stomp_message/stomp_participant.rb
10
14
  lib/stomp_message/message.rb
11
15
  log/debug.log
12
16
  bin/stomp_message_send.rb
data/README.txt CHANGED
@@ -1 +1,26 @@
1
- README
1
+ A basic package for sending and receiving stomp messages
2
+
3
+
4
+ A server class will listen to topics (StompMessage::StompServer)
5
+
6
+
7
+ A class to easily send (and receive messages from the server). Since the messsage protocol is semi one way to receive a message you reply to a distinct topic and a call back is executed.
8
+
9
+ The code below creates server class and sends message/gets response.
10
+
11
+ IMPORTANT apachemq or some other messaging platform must be running :)
12
+
13
+ args={:topic => '/topic/test'}
14
+ ss=StompMessage::StompStatisticsServer.new(args)
15
+ ss_thread = Thread.new {
16
+ ss.run }
17
+
18
+ msg=StompMessage::Message.new('stomp_REPORT', "body")
19
+
20
+ puts "creating send topic"
21
+ send_topic=StompMessage::StompSendTopic.new(args)
22
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m|
23
+ puts "#{m.to_s}"
24
+ msg=StompMessage::Message.load_xml(m)
25
+ assert msg.command== 'stomp_REPLY', "command wrong"
26
+ }
@@ -8,6 +8,12 @@ require 'stomp_message'
8
8
  def usage
9
9
  puts "Usage: stomp_message_send.rb -t topic -m command -b body -h host -p port "
10
10
  puts "to turn on debug: stomp_message_send.rb -t '/topic/sms' -m stomp_DEBUG -b nil"
11
+ puts "for statistics report: stomp_message_send.rb -t '/topic/sms' -m stomp_REPORT -b nil -a true"
12
+ puts "for statistics report: stomp_message_send.rb -t '/topic/sms' -m stomp_REPORT -b nil -a true"
13
+ puts "for ping test: stomp_message_send.rb -t '/topic/sms' -m stomp_PING -b nil -a true"
14
+ puts "for email ping test: stomp_message_send.rb -t '/topic/sms' -m stomp_PING -b nil -a true -d comma_separated_email_addresses"
15
+ puts "-a flag for response"
16
+
11
17
  exit
12
18
  end
13
19
  def parse_options(params)
@@ -17,23 +23,35 @@ def parse_options(params)
17
23
  #puts "paramsp is #{paramsp}"
18
24
  topic_flag=command_flag=body_flag=true
19
25
  temp_hash = {}
26
+ temp_hash[:ack]= 'false' # no ack by default
27
+ temp_hash[:msisdn] = 'not_defined'
28
+ email_flag=false
20
29
  opts.on("-h","--host VAL", String) {|val| temp_hash[:host ] = val
21
- puts "host is #{val}"
30
+ # puts "host is #{val}"
22
31
  }
32
+ opts.on("-d","--email VAL", String) {|val| temp_hash[:email ] = val
33
+ puts "email address is #{val}"
34
+ # puts "host is #{val}"
35
+ }
36
+
23
37
  opts.on("-p","--port VAL", String) {|val| temp_hash[:port ] = val
24
- puts "port is #{val}"
38
+ # puts "port is #{val}"
25
39
  }
40
+ opts.on("-a","--ack VAL", String) {|val| temp_hash[:ack ] = val
41
+ puts "ack is #{val}"
42
+ }
43
+
26
44
  opts.on("-u","--user VAL", String) {|val| temp_hash[:user ] = val
27
- puts "user is #{val}"
45
+ # puts "user is #{val}"
28
46
  user_flag=false }
29
47
  opts.on("-t","--topic VAL", String) {|val| temp_hash[:topic ] = val
30
- puts "topic is #{val}"
48
+ # puts "topic is #{val}"
31
49
  topic_flag=false }
32
50
  opts.on("-m","--msg_command VAL", String) {|val| temp_hash[:command ] = val
33
- puts "command is #{val}"
51
+ # puts "command is #{val}"
34
52
  command_flag=false }
35
53
  opts.on("-b","--body VAL", String) {|val| temp_hash[:body ] = val
36
- puts "body is #{val}"
54
+ # puts "body is #{val}"
37
55
  body_flag=false }
38
56
  #opts.on("-d","--database VAL", String) {|val| temp_hash[:db ] = val }
39
57
  #opts.on("-p","--password VAL", String) {|val| temp_hash[:password ] = val }
@@ -48,16 +66,50 @@ def parse_options(params)
48
66
  require 'pp'
49
67
  options = arg_hash
50
68
  # set up variables using hash
51
- host = options[:host]==nil ? 'localhost' : options[:host]
52
- port = options[:port]==nil ? '61613' : options[:port]
53
- puts "host is: #{host} port is #{port}"
54
- conn = Stomp::Connection.open '', '', host, port, false
55
- # self.conn.subscribe @@TOPIC, { :ack =>"auto" }
56
- puts "finished initializing"
57
- m=StompMessage::Message.new(options[:command], options[:body])
58
- # puts "message body is #{msgbody}"
59
- conn.send options[:topic], m.to_xml, {'persistent'=>'false'}
60
-
69
+ msg_sender=StompMessage::StompSendTopic.new(options)
70
+ msg_sender.setup_auto_close
71
+ m=StompMessage::Message.new(options[:command],options[:body])
61
72
  puts "message is: #{m.to_xml}"
62
- puts 'successfully sent messsage!!!'
73
+ # billing_sender.send_topic(m.body, arg_hash[:msisdn])
74
+ # m=StompMessage::Message.new('stomp_BILLING', msg)
75
+ header = {}
76
+ header[:msisdn]=arg_hash[:msisdn]
77
+ msg_received_flag=false
78
+ msg_data=nil
79
+ case options[:ack].downcase
80
+ when 'true'
81
+ msg_sender.send_topic_acknowledge(m,header,50) { |msg| # puts 'in handle action block'
82
+ puts 'MESSAGE RECEIVED ----'
83
+ msg_received_flag=true
84
+ msg_data=msg
85
+ puts "handle action block msg is #{msg.to_s}" }
86
+ begin
87
+ Timeout::timeout(100) {
88
+ while true
89
+ putc '.'
90
+ exit(0) if msg_received_flag
91
+ sleep(1)
92
+ end }
93
+ rescue SystemExit
94
+
95
+ rescue Exception => e
96
+ puts "exception #{e.message} class: #{e.class}"
97
+ puts "no receipt"
98
+ # raise "timeout"
99
+ #ensure
100
+ # msg_sender.setup_auto_close
101
+ end
102
+
103
+
104
+ else
105
+ msg_sender.send_topic(m,header)
106
+
107
+ end
108
+
109
+ msg_sender.send_email_stomp("scott.sproule@cure.com.ph","STOMP MSG", options[:email],
110
+ m.command, msg_data) if msg_received_flag and options[:email]!=nil
111
+
112
+
113
+
114
+ puts '-------------finished processing!!!'
63
115
 
data/config/hoe.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'stomp_message/version'
2
2
 
3
- AUTHOR = 'FIXME full name' # can also be an array of Authors
4
- EMAIL = "FIXME email"
5
- DESCRIPTION = "description of gem"
3
+ AUTHOR = 'scott sproule' # can also be an array of Authors
4
+ EMAIL = "scott dot sproule at ficonab dot com"
5
+ DESCRIPTION = "handling stomp messages and servers"
6
6
  GEM_NAME = 'stomp_message' # what ppl will type to install your gem
7
- RUBYFORGE_PROJECT = 'stomp_message' # The unix name for your project
7
+ RUBYFORGE_PROJECT = 'stompmessage' # The unix name for your project
8
8
  HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
9
  DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
10
 
@@ -1,29 +1,97 @@
1
1
  # basic format for stomp messages
2
2
  require 'rexml/document'
3
+ #require 'xml_helper.rb'
4
+ require 'rubygems'
5
+ gem 'stomp_message'
6
+ require 'stomp_message'
3
7
  module StompMessage
8
+ module XmlHelper
9
+ # create elements from instance variables... (instance variablesneed to be set)
10
+ #array variables need to end in s (eg mssidns) and are handled recurvisely
11
+ def xml_instance_variable(iv_input)
12
+ iv=iv_input.delete('@')
13
+ class_var=eval("self.#{iv}.class")
14
+ # puts "class is #{class_var} "
15
+ iv_xml=[]
16
+ case
17
+ when class_var==Array
18
+ #puts "in array with #{iv}"
19
+ len=iv.size
20
+ check_for_s=iv[len-1]
21
+ # puts "iv #{iv}: len is #{len} last digit is #{check_for_s}"
22
+ raise "not terminate in s or too short" if len <=1 or check_for_s!=115
23
+ the_array = eval("self.#{iv}")
24
+ iv_short = iv[0..len-2]
25
+ #puts "iv short is #{iv_short}"
26
+ the_array.each {|e|
27
+ iv_xml << create_element(iv_short,e) }
28
+
29
+ else
30
+ val= eval "self.#{iv}"
31
+ iv_xml[0]= create_element(iv.to_s,val)
32
+ end
33
+ return iv_xml
34
+ end
35
+ def create_element(element_name, element_value)
36
+ # puts "in create element #{element_name}: #{element_value}"
37
+ element_xml= REXML::Element.new element_name
38
+ element_xml.text=element_value
39
+ element_xml
40
+ end
41
+ # add all elements to top variable
42
+ def add_elements(top)
43
+ elements = []
44
+ self.instance_variables.each {|iv| xml_instance_variable(iv).each {|x| elements << x}
45
+
46
+
47
+ }
48
+ elements.each {|e| top.add_element e}
49
+ top
50
+ end
51
+
52
+ end
4
53
  class Message
5
- attr_accessor :command, :body
6
- def initialize(cmd, body=nil)
7
- self.command=cmd
8
- self.body=body
54
+ include StompMessage::XmlHelper
55
+ attr_accessor :__stomp_msg_command, :__stomp_msg_body
56
+ def initialize(cmd, bdy=nil)
57
+ raise 'command nil' if cmd==nil
58
+ self.__stomp_msg_command = cmd==nil ? '' : cmd.to_s
59
+ self.__stomp_msg_body = bdy==nil ? '' : bdy.to_s
60
+
9
61
  end
10
- def to_xml
11
- msg_xml = REXML::Element.new "message"
12
- command_xml = REXML::Element.new "command"
13
- command_xml.text=self.command
14
- body_xml =REXML::Element.new "body"
15
- body_xml.text=self.body
16
- msg_xml.add_element command_xml
17
- msg_xml.add_element body_xml
18
- msg_xml.to_s
62
+ def body
63
+ self.__stomp_msg_body
64
+ end
65
+ def command
66
+ self.__stomp_msg_command
67
+ end
68
+
69
+ def to_xml
70
+ doc=REXML::Document.new()
71
+ msg_xml = REXML::Element.new "stomp_msg_message"
72
+ doc.add_element(msg_xml)
73
+ msg3 = self.add_elements(REXML::Element.new("__instance_variables"))
74
+ doc.root.add_element(msg3)
75
+ output =""
76
+ doc.write output
77
+ output
19
78
  # doc= REXML::Document.new sms_xml.to_s
20
79
  # doc.to_s
21
80
  end
81
+
22
82
  def self.load_xml(xml_string)
83
+ begin
23
84
  doc=REXML::Document.new(xml_string)
24
- command=REXML::XPath.first(doc, "//command").text
25
- body=REXML::XPath.first(doc, "//body").text
26
- sms=StompMessage::Message.new(command,body)
85
+ command=REXML::XPath.first(doc, "//__stomp_msg_command").text
86
+ # puts "load_xml command is #{command}"
87
+ body=REXML::XPath.first(doc, "//__stomp_msg_body").text
88
+ # puts "load_xml body is #{body}"
89
+ sms=StompMessage::Message.new(command, body)
90
+ rescue Exception => e
91
+ puts "Exception in load xml:#{xml_string}"
92
+ puts "message #{e.message}"
93
+ end
94
+ sms
27
95
  end
28
96
  end
29
97
  end
@@ -0,0 +1,102 @@
1
+ require 'rubygems'
2
+ gem 'stomp_message'
3
+ require 'stomp_message'
4
+ require 'timeout'
5
+ gem 'openwferu'
6
+ require 'openwfe/participants/participants'
7
+
8
+
9
+ module OpenWFE
10
+
11
+ #
12
+ # Participant to send/receive work items to stomp message servers applications
13
+ # send message and asynch wait for response
14
+ #
15
+ # Timeoout may need to be changed
16
+ #
17
+ # On the return side, you can override the method handle_call_result
18
+ # for better mappings between messages calls and the workitems.
19
+ #
20
+ class StompParticipant
21
+ include LocalParticipant
22
+ attr_accessor :timeout_val, :options, :msg_sender
23
+ def initialize(topic, host='localhost', port=61613, timeout=4, &block)
24
+
25
+
26
+ self.options={}
27
+ self.options[:host]=host
28
+ self.options[:port]=port
29
+ self.options[:topic]=topic
30
+ self.timeout_val = timeout
31
+ self.msg_sender=StompMessage::StompSendTopic.new(self.options)
32
+ self.msg_sender.setup_auto_close
33
+ end
34
+
35
+ #
36
+ # The method called by the engine when the flow reaches an instance
37
+ # of this Participant class.
38
+ #
39
+ def consume (workitem)
40
+
41
+ m=prepare_call_params(workitem)
42
+ puts "message is: #{m.to_xml}"
43
+ # billing_sender.send_topic(m.body, arg_hash[:msisdn])
44
+ # m=StompMessage::Message.new('stomp_BILLING', msg)
45
+ header={}
46
+ # header[:msisdn]=workitem.attributes[:msisdn]
47
+ begin
48
+ Timeout::timeout(self.timeout_val) {
49
+ self.msg_sender.send_topic_acknowledge(m,header) {
50
+ |msg| workitem=handle_call_result(msg, workitem)
51
+ }
52
+ }
53
+ rescue Timeout::Error
54
+ puts "STOMP participant:: consume(wi) exception"
55
+ workitem.attributes["__result__"]=false
56
+ workitem.attributes["stomp_TIMEOUT"]=true
57
+ # raise "timeout"
58
+ ensure
59
+ reply_to_engine(workitem)
60
+ end
61
+
62
+
63
+
64
+ end
65
+
66
+ #
67
+ # The base implementation :prepares the message
68
+ # param there is a workitem field with the same name.
69
+ #
70
+ # Feel free to override this method.
71
+ #
72
+ def prepare_call_params (workitem)
73
+ m=StompMessage::Message.new(workitem.attributes[:command].to_s,
74
+ workitem.attributes[:body].to_s)
75
+ m
76
+ # puts "message is: #{m.to_xml}"
77
+ end
78
+
79
+ #
80
+ # This implementation simply stuffs the result into the workitem
81
+ # as an attribute named "__result__".
82
+ #
83
+ # Feel free to override this method.
84
+ #
85
+ def handle_call_result (result, workitem)
86
+
87
+ puts 'in handle action block'
88
+ puts 'MESSAGE RECEIVED ----'
89
+ m= StompMessage::Message.load_xml(result)
90
+ workitem.attributes["__result__"]=m.to_xml
91
+ workitem.attributes["command"]=m.command.to_s
92
+ workitem.attributes["body"]=m.body.to_s
93
+ puts "wi #{workitem.attributes.to_s}"
94
+ workitem
95
+
96
+ end
97
+
98
+
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,173 @@
1
+ require 'yaml'
2
+ require 'rubygems'
3
+ gem 'stomp'
4
+ require 'stomp'
5
+ require 'net/smtp'
6
+ require 'net/http'
7
+ require 'socket'
8
+
9
+ # This
10
+ module StompMessage
11
+ # this class manages sending and receiving messages. It uses passed code block to execute received code
12
+ class StompSendTopic
13
+ attr_accessor :conn, :topic, :host, :port, :login, :password, :url
14
+ #need to define topic, host properly
15
+
16
+ def initialize(options={})
17
+ # set up variables using hash
18
+ @close_ok=false
19
+ init_vars(options)
20
+ # puts "host is: #{host} port is #{port}"
21
+ # using url as flag wrong
22
+ # self.conn = Stomp::Client.new(self.login, self.password, self.host, self.port, false) if self.url ==nil
23
+
24
+ puts "#{self.class}: Initialized host is: #{self.host} port is #{self.port} topic is #{self.topic} login #{self.login} pass: #{self.password}"
25
+ # scott old self.conn.subscribe( self.topic, { :ack =>"auto" }) { |m|
26
+ # self.conn.subscribe( self.topic, { :ack =>"client" }) { |m|
27
+ # # puts "#{self.class} msg: #{m.to_s}"
28
+ # }
29
+ # setup_auto_close
30
+ end
31
+ def init_vars(options)
32
+ self.login = options[:login]==nil ? '' : options[:login]
33
+ self.url = options[:url]== nil ? nil : options[:url]
34
+ self.password = options[:password]==nil ? '' : options[:password]
35
+ self.host = options[:host]==nil ? 'localhost' : options[:host]
36
+ self.port = options[:port]==nil ? '61613' : options[:port]
37
+ self.topic = options[:topic]==nil ? '/topic/undefined' : options[:topic]
38
+ self.conn=nil
39
+ puts "self url is #{self.url}"
40
+ end
41
+ # only call this once
42
+ def setup_auto_close
43
+
44
+ at_exit { puts "#{self.class}: auto close exit block"
45
+ close_topic
46
+ disconnect_stomp } if !@close_ok
47
+ @close_ok=true
48
+ end
49
+ #manage timeout etc...
50
+ def self.open_connection(old_conn,login,pass,host,port)
51
+ conn=old_conn
52
+ flag=false
53
+ begin
54
+ conn.close if conn!=nil
55
+
56
+ Timeout::timeout(5) {
57
+ conn=nil
58
+ conn = Stomp::Client.new(login, pass, host, port, false)
59
+ flag=true
60
+ }
61
+
62
+ rescue Timeout::Error
63
+ puts "Timeout error: exception retrying flag is: #{flag}"
64
+ retry if !flag
65
+ # raise "timeout"
66
+
67
+ end
68
+ conn
69
+ end
70
+ def open_connection
71
+ self.conn = StompMessage::StompSendTopic.open_connection(@conn, self.login, self.password, self.host, self.port) if self.conn==nil
72
+ end
73
+ # close the topic
74
+ def close_topic
75
+ self.conn.unsubscribe(self.topic) if self.conn !=nil
76
+ # self.conn=nil
77
+ end
78
+ #disconnect the connection
79
+ def disconnect_stomp
80
+ close_topic
81
+ puts "#{self.class} closing connection"
82
+ self.conn.close() if self.conn !=nil
83
+ end
84
+ # post stomp message to url
85
+ def post_stomp(msg,headers)
86
+
87
+ response_header = {"Content-type" => "text/xml"}
88
+ response_header.merge headers
89
+ ht =Net::HTTP.start(self.host,self.port)
90
+ r=ht.post(self.url,msg.to_xml,response_header)
91
+ puts "url was: #{url}"
92
+ puts "result: #{r.to_s}"
93
+ r
94
+ end
95
+ def send_topic(msg, headers, &r_block)
96
+ # m=StompMessage::Message.new('stomp_BILLING', msg)
97
+ open_connection
98
+ more_headers= {'persistent'=>'false' }
99
+ more_headers.merge headers
100
+ self.conn.send(self.topic, msg.to_xml, more_headers, &r_block)
101
+ # Thread.pass
102
+ end #send_sms
103
+ # be careful with the receipt topic calculations... strange errors onactive mq
104
+ def send_topic_acknowledge(msg, headers, timeout=4)
105
+ #m=StompMessage::Message.new('stomp_BILLING', msg)
106
+ open_connection
107
+ s=rand*20 # scott - used to be 1000 but seem to create connections on activemq
108
+ # open new topic to listen to reply...
109
+ # was this but jms seems to blow up receipt_topic="/topic/receipt/client#{s.to_i}"
110
+ receipt_topic="/topic/rcpt_client#{s.to_i}"
111
+ receipt_flag = false
112
+ # internal_conn = Stomp::Connection.open '', '', self.host, self.port, false
113
+ self.conn.subscribe( receipt_topic, { :ack =>"client" }) {|msg|
114
+ begin
115
+ Timeout::timeout(timeout) {
116
+ self.conn.acknowledge(msg,msg.headers)
117
+ msg2= msg.body
118
+ yield msg2
119
+ }
120
+ rescue Exception => e
121
+ puts "exception #{e.message}"
122
+ # raise "timeout"
123
+ ensure
124
+ receipt_flag=true
125
+ self.conn.unsubscribe receipt_topic
126
+ end
127
+ }
128
+
129
+
130
+ more_headers= {'persistent'=>'false', 'reply-to' => "#{receipt_topic}" }
131
+ more_headers.merge headers
132
+ self.conn.send(self.topic, msg.to_xml, more_headers )
133
+ Thread.new { sleep(timeout)
134
+ puts "calling unsubscribe on #{receipt_topic}" if !receipt_flag
135
+ self.conn.unsubscribe receipt_topic if !receipt_flag
136
+ }
137
+ end #send_sms
138
+ def send_email_stomp(from, from_alias, to, subject, message)
139
+ StompMessage::StompSendTopic.send_email_stomp(from,from_alias,to,subject,message)
140
+ end
141
+ def self.send_email_stomp(from, from_alias, to, subject, message)
142
+ #to_csv_array=to.each.join(",")
143
+ msg = <<EOF__RUBY_END_OF_MESSAGE
144
+ From: #{from_alias} <#{from}>
145
+ To: #{to}
146
+ Subject: #{subject} #{Time.now}
147
+ Comand ----------
148
+ #{subject}
149
+ RESPONSE -----------
150
+ #{message}
151
+ EOF__RUBY_END_OF_MESSAGE
152
+ recipients=to.split(',')
153
+ recipients.each {|r| StompMessage::StompSendTopic.send_email(from,r,msg)}
154
+
155
+ end
156
+ def self.send_email(from,to,msg)
157
+ case Socket.gethostname
158
+ when "svbalance.cure.com.ph"
159
+ smtp_host='mail2.cure.com.ph'
160
+ when "Scotts-Computer.local"
161
+ smtp_host='mail2.cure.com.ph'
162
+ else
163
+ smtp_host='localhost'
164
+ end
165
+
166
+ Net::SMTP.start(smtp_host) { |smtp|
167
+ smtp.send_message(msg, from, to)
168
+ }
169
+ end
170
+
171
+ end # stomp send topic
172
+
173
+ end #module
@@ -0,0 +1,176 @@
1
+ # basic format for stomp messages
2
+ require 'rexml/document'
3
+ require 'socket'
4
+ # basic server class for listening to topics
5
+ module StompMessage
6
+ class StompServer
7
+ attr_accessor :conn, :topic, :msg_count, :exception_count, :host, :port, :login, :password, :queue, :thread_count, :mythreads
8
+ #need to define topic, host properly
9
+ @debug=false
10
+
11
+ def initialize(options={})
12
+ @my_hostname = Socket.gethostname
13
+ self.login = options[:login]==nil ? '' : options[:login]
14
+ num = options[:thread_count]==nil ? '2' : options[:thread_count]
15
+ self.thread_count=num.to_i
16
+ self.password = options[:password]==nil ? '' : options[:password]
17
+ self.host = options[:host]==nil ? 'localhost' : options[:host]
18
+ self.port = options[:port]==nil ? '61613' : options[:port]
19
+ self.topic = options[:topic]==nil ? '/topic/please_define' : options[:topic]
20
+ puts "#{self.class}: message broker host is: #{host} port is #{port} topic is: #{self.topic} threads #{self.thread_count} my host: #{@my_hostname}"
21
+ self.msg_count = self.exception_count=0
22
+ self.queue= Queue.new
23
+ self.conn=nil
24
+ connect_connection
25
+ # self.conn = Stomp::Client.new self.login, self.password, self.host, self.port, false
26
+ # self.conn = Stomp::Connection.open self.login, self.password, self.host , self.port, false
27
+ connect_topic
28
+ @debug=false
29
+ puts "finished StompServer initializing"
30
+ setup_auto_close
31
+ trap("INT") { puts "#{Time.now}: #{self.class} in interrupt trap\n"
32
+ #close_topic
33
+ #disconnect_stomp
34
+ #setup_auto_close already done in INIT
35
+ exit(0)}
36
+ end
37
+ def connect_connection
38
+ self.conn = StompMessage::StompSendTopic.open_connection(self.conn, self.login, self.password, self.host, self.port)
39
+ end
40
+ def setup_auto_close
41
+ at_exit { puts "#{self.class}: in at exit block"
42
+ close_topic
43
+ disconnect_stomp
44
+ # self.mythreads.each {|t| t.raise "auto exit" }
45
+ }
46
+ end
47
+ def disconnect_stomp
48
+ puts "#{self.class} #{@my_hostname} closing connection"
49
+ self.conn.close() if self.conn !=nil
50
+ end
51
+ def send_reply(headers,msg)
52
+ reply_topic=headers['reply-to']
53
+ # puts "headers: #{headers['msisdn']} reply topic #{headers['reply-to']}"
54
+ # internal_conn = Stomp::Connection.open '', '', self.host, self.port, false
55
+ # scott old auto
56
+ # scott not needed self.conn.subscribe(reply_topic, { :ack =>"client" }) {|m| puts "yes boring should not be called: #{m.to_s}"
57
+ # }
58
+ # m=StompMessage::Message.new('stomp_REPLY', msg_body)
59
+ response_header = {'persistent'=>'false'}
60
+ response_header.merge headers
61
+ self.conn.send(reply_topic, msg.to_xml, response_header )
62
+ # self.conn.unsubscribe reply_topic #testing shoul call this but not certain about it
63
+ # internal_conn.disconnect
64
+ end
65
+ # name is message command
66
+ def connect_topic
67
+ # scott old ack auot
68
+ # see http://activemq.apache.org/stomp.html for activemq.dispathAsync settig
69
+ self.conn.subscribe( self.topic, { :ack =>"client" , 'activemq.dispatchAsync' => 'false'}) { |msg|
70
+
71
+ self.msg_count+=1
72
+ begin
73
+ self.conn.acknowledge(msg,msg.headers)
74
+ self.queue << msg
75
+
76
+ rescue Exception => e
77
+ self.exception_count+=1
78
+ puts " Thread: :exception found #{e.backtrace}"
79
+ puts "Thread: :exception messag #{e.message}"
80
+ end
81
+
82
+ }
83
+ end
84
+ # close the topic, override if necessary
85
+ def close_topic
86
+ puts "#{self.class} #{@my_hostname} unsubscribing #{self.topic}"
87
+ self.conn.unsubscribe self.topic if self.conn!=nil
88
+ end
89
+ def stomp_PING(msg,stomp_msg)
90
+ body="ALIVE Class: #{self.class.to_s} listing to: #{self.topic} on host #{@my_hostname} msg_count #{self.msg_count} exception_count #{self.exception_count}"
91
+ reply_msg = StompMessage::Message.new('stomp_REPLY', body)
92
+ send_reply(stomp_msg.headers,reply_msg) if stomp_msg.headers['reply-to']!=nil
93
+ end
94
+ def stomp_DEBUG(msg,stomp_msg)
95
+ @debug=!@debug
96
+ puts "debug flag is now #{@debug}"
97
+ end
98
+ def method_missing(name, *args)
99
+ puts "Method missing called: #{name}"
100
+ puts "Likely invalid message recieved"
101
+ end
102
+ # monitor queue
103
+ def monitor_queue_status
104
+ puts "starting up queue status"
105
+ self.mythreads << Thread.new { while true
106
+ sleep(30)
107
+ puts "QUEUE size is: #{self.queue.size}"
108
+ # puts "-------conn var is on #{self.conn.inspect}"
109
+ if !self.conn.open?
110
+ puts "restarting connection"
111
+ self.conn=nil
112
+ connect_connection
113
+ connect_topic
114
+ end
115
+ end # while
116
+ }
117
+ end
118
+ def handle_message(msg)
119
+ puts "STOMP message frame is : #{msg} " if @debug
120
+ m=StompMessage::Message.load_xml(msg.body)
121
+ # puts "Message is: #{m.to_xml}" if @debug
122
+ puts "Message type is #{m.command}" if @debug
123
+ send(m.command, m, msg) # effectively case statement (Should SCREEN HERE)
124
+ # puts "sms text is: #{sms.text} dest is: #{sms.destination} result is: #{res}"
125
+ end
126
+ def run
127
+ self.mythreads = []
128
+ 1.upto(self.thread_count) { |c| # create the threads here
129
+ puts "creating thread: #{c}"
130
+ self.mythreads << Thread.new(c) { |ctmp|
131
+ while true
132
+
133
+ begin
134
+ msg=self.queue.pop
135
+ handle_message(msg)
136
+
137
+ rescue Exception => e
138
+ self.exception_count+=1
139
+ puts " Thread: #{ctmp} :exception found #{e.backtrace}"
140
+ puts "Thread: #{ctmp} :exception messag #{e.message}"
141
+ result = "-----------EXCEPTION FOUND----------\n"
142
+ result << "ALIVE Class: #{self.class.to_s} listing to: #{self.topic} on host #{@my_hostname} msg_count #{self.msg_count} exception_count #{self.exception_count}\n"
143
+ result << "-----exception data--------\n"
144
+ result << " Thread: #{ctmp} :exception found #{e.backtrace}\n"
145
+ result << "Thread: #{ctmp} :exception messag #{e.message}"
146
+ begin
147
+ StompMessage::StompSendTopic.send_email_stomp("scott.sproule@cure.com.ph",
148
+ "STOMP EXCEPTION", "scott.sproule@cure.com.ph","Thread: #{ctmp} :exception messag #{e.message}", result)
149
+ rescue Exception => e
150
+ puts "Can not send email, please check smtp host setting"
151
+ end
152
+ end
153
+ # Thread.pass
154
+ end
155
+ }
156
+ }
157
+ monitor_queue_status
158
+ self.mythreads.each { |t| t.join }
159
+
160
+
161
+ # msg = self.conn.receive
162
+ # puts "after receive"
163
+ # self.msg_count+=1
164
+ # begin
165
+ # handle_message(msg)
166
+ # rescue Exception => e
167
+ # self.exception_count+=1
168
+ # puts "exception found #{e.backtrace}"
169
+ # puts "exception messag #{e.message}"
170
+ # end
171
+ # end # while
172
+
173
+ # t.join # wait for t to die..
174
+ end #run
175
+ end
176
+ end
@@ -0,0 +1,83 @@
1
+ # basic format for stomp messages
2
+ require 'rexml/document'
3
+ # basic statistics class for listening to topics
4
+ module StompMessage
5
+ # statistics listening class. Updates the statistics at every message/event
6
+ class StompStatisticsServer < StompMessage::StompServer
7
+ attr_accessor :tags, :statistics
8
+ def setup_var
9
+ self.tags = %w(hour day week month)
10
+ self.statistics = Hash.new
11
+ self.tags.each { |tagv| statistics[tagv] = Hash.new(0) }
12
+ @old_exception_count=0
13
+ end
14
+ def initialize(options={})
15
+ super(options)
16
+ setup_var
17
+ end
18
+ # update the statistics by tag value (eg hour)
19
+ def increment_stats(tag, element)
20
+ self.statistics[tag][element]+=1
21
+ end
22
+ # reset element to zero (eg reset hour to zero)
23
+ def reset_element(tag, element)
24
+ self.statistics[tag][element]=0
25
+ end
26
+ # increment all the tags with a new element value . Hash is zero if not found
27
+ def tag_increment(element)
28
+ self.tags.each {|tagv| puts "incrementing #{tagv} element: #{element}" if @debug
29
+ increment_stats(tagv,element)}
30
+
31
+ end
32
+
33
+ def handle_message(msg)
34
+ puts "in handle message stomp stat server" if @debug
35
+ m=StompMessage::Message.load_xml(msg.body)
36
+ tag_increment(m.command)
37
+ super(msg)
38
+ tag_increment('msg_count')
39
+ tag_increment('exception_count') if @old_exception_count!= self.exception_count
40
+ @old_exception_count= self.exception_count
41
+
42
+ end
43
+ def stomp_PING(msg, stomp_msg)
44
+ puts "stomp PING: #{msg.body}" if @debug
45
+ #do not reply as statistic servers do no respond to pings. they respond to stomp_REPORT
46
+ end
47
+ #def create_statistics(msg) # k[it]=0 if !k.key?(it)
48
+ # self.tags.each { |tagv| self.statistics[tagv].each {|k|
49
+ # internal_tags = %w(msg_count, exception_count )
50
+ # internal_tags.each { |it|
51
+ # data= eval("self.#{it}")
52
+ # k[it]= data-k[it]
53
+ # puts "it is #{it}: data is #{data} k[it] is #{k[it]}"
54
+ # }
55
+ # }
56
+ # }
57
+ #
58
+ #end
59
+ def stomp_REPORT(msg, stomp_msg)
60
+
61
+ result= " Stomp Report #{Time.now}\n"
62
+ result << " Messages processed: #{self.msg_count} \n"
63
+ flag = self.exception_count!=nil ? '' : 'IMPORTANT'
64
+ result << "#{flag} Exceptions processed: #{self.exception_count}\n "
65
+ self.tags.each { |tagv| # run through each tag (hour, day etc)
66
+ result << "#{tagv} Latests Statistics\n"
67
+ self.statistics[tagv].each {|k,v| result << " ----- #{k}: #{v}\n"}
68
+ }
69
+ puts result if @debug
70
+ reply_msg = StompMessage::Message.new('stomp_REPLY', "#{result}")
71
+ send_reply(stomp_msg.headers,reply_msg) if stomp_msg.headers['reply-to']!=nil
72
+ result
73
+ end
74
+ def stomp_RESET(msg, stomp_msg)
75
+ puts "stomp reset: #{msg.body}" if @debug
76
+ reset_tag = msg.body.to_s if self.tags.include?(msg.body.to_s)
77
+ puts " --- RESET TAG: #{reset_tag}"
78
+ self.statistics[reset_tag].each { |k,v| self.statistics[reset_tag][k]=0
79
+ puts "RESET: tag #{k} value is zero"
80
+ } if self.tags.include?(msg.body.to_s)
81
+ end
82
+ end
83
+ end
@@ -1,8 +1,8 @@
1
1
  module StompMessage #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 0
5
- TINY = 3
4
+ MINOR = 1
5
+ TINY = 2
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,5 +1,12 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
-
2
+ class OpenWFE::StompParticipant
3
+ def reply_to_engine(workitem)
4
+ puts "hello from test reply to engine"
5
+ end
6
+ end
7
+ require 'rubygems'
8
+ gem 'openwferu'
9
+ require 'openwfe/workitem'
3
10
  class TestStompMessage < Test::Unit::TestCase
4
11
 
5
12
  def setup
@@ -10,6 +17,66 @@ class TestStompMessage < Test::Unit::TestCase
10
17
  m2=StompMessage::Message.new("command")
11
18
  assert m1.to_xml==m2.to_xml, "xml generation wrong"
12
19
  end
20
+ def test_object_create2
21
+ m1=StompMessage::Message.new("command",true)
22
+ m2=StompMessage::Message.new("command",true)
23
+ assert m1.to_xml==m2.to_xml, "xml generation wrong"
24
+ m1=StompMessage::Message.new("command",false)
25
+ m2=StompMessage::Message.new("command",false)
26
+ assert m1.to_xml==m2.to_xml, "xml generation wrong"
27
+ end
28
+ def test_object_nil
29
+ begin
30
+ m1=StompMessage::Message.new(nil)
31
+ rescue Exception => e
32
+ assert true, 'raised exception'
33
+ end
34
+ end
35
+ class M3 < StompMessage::Message
36
+ attr_accessor :msisdns, :bad , :s
37
+ def initialize(cmd)
38
+ super(cmd)
39
+ self.msisdns=[]
40
+ self.s= []
41
+ self.bad = []
42
+ self.msisdns << "test"
43
+ self.msisdns << "test2"
44
+ self.bad << "test"
45
+ self.s << "test"
46
+ end
47
+ end # class M3
48
+ class M4 < StompMessage::Message
49
+ attr_accessor :msisdns
50
+ def initialize(cmd)
51
+ super(cmd, "body")
52
+ self.msisdns=[]
53
+ self.msisdns << "test"
54
+ self.msisdns << "test2"
55
+ end
56
+ end # class M3
57
+ def test_xml1_helper
58
+ puts "XML Helper test"
59
+ m2=M3.new("command")
60
+ begin
61
+ res=m2.to_xml
62
+ # puts "m2 xml is #{res}"
63
+ rescue Exception => e
64
+ puts "found exception #{e.message}"
65
+ assert e.message=="not terminate in s or too short", "bad message"
66
+ end
67
+ end
68
+ def test_xml2_helper
69
+ puts "XML2 Helper test"
70
+ m2=M4.new("command")
71
+ begin
72
+ res=m2.to_xml
73
+ puts "m2 xml is #{res}"
74
+ rescue Exception => e
75
+ puts "found exception #{e.message}"
76
+ assert false, "bad message #{e.message}"
77
+ end
78
+
79
+ end
13
80
  def test_object_duplication
14
81
  m1=StompMessage::Message.new("command")
15
82
  m2=StompMessage::Message.new("command")
@@ -17,8 +84,131 @@ class TestStompMessage < Test::Unit::TestCase
17
84
  assert m1.to_xml==m2.to_xml, "xml generation wrong"
18
85
  assert m3.to_xml==m2.to_xml, "xml generation wrong"
19
86
  m2=StompMessage::Message.new("command","body")
20
- assert m3.to_xml!=m2.to_xml, "xml generation wrong"
87
+ assert m3.to_xml!=m2.to_xml, "xml generation wrong"
21
88
  m4=StompMessage::Message.load_xml(m2.to_xml)
22
- assert m4.body==m2.body, "xml generation wrong"
89
+ puts "CHECK THIS m2 is: #{m2.to_xml} m4 is #{m4.to_xml}"
90
+ assert m4.body==m2.body, "xml generation wrong"
91
+ end
92
+ def test_statistics_server
93
+ #note activemq or stomp message bus needs to be running
94
+ args={:topic => '/topic/test'}
95
+ ss=StompMessage::StompStatisticsServer.new(args)
96
+ ss_thread = Thread.new {
97
+ ss.run }
98
+ assert ss.topic=='/topic/test', "topic not set properly"
99
+ sleep(1)
100
+ msg=StompMessage::Message.new('stomp_REPORT', "body")
101
+ assert msg.command=='stomp_REPORT', "message command not correct"
102
+ puts "creating send topic"
103
+ send_topic=StompMessage::StompSendTopic.new(args)
104
+ puts "send topic NO ack"
105
+ send_topic.send_topic(msg,{ :msisdn => "639999"})
106
+ puts "send topic WITH ack"
107
+ msg_reply=''
108
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m| assert true
109
+ puts "#{m.to_s}"
110
+ msg_reply=StompMessage::Message.load_xml(m)
111
+
112
+ }
113
+ sleep(3)
114
+ assert msg_reply.command== 'stomp_REPLY', "command wrong"
115
+ ss_thread.kill
116
+ end
117
+ def test_base_server
118
+ #note activemq or stomp message bus needs to be running
119
+ args={:topic => '/topic/test'}
120
+ ss=StompMessage::StompServer.new(args)
121
+ ss_thread = Thread.new {
122
+ ss.run }
123
+ sleep(3)
124
+ assert ss.topic=='/topic/test', "topic not set properly"
125
+ msg=StompMessage::Message.new('stomp_PING', "body")
126
+ assert msg.command=='stomp_PING', "message command not correct"
127
+ puts "creating ping messager topic"
128
+ send_topic=StompMessage::StompSendTopic.new(args)
129
+ puts "send ping WITH ack"
130
+ msg_reply=''
131
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m| assert true
132
+ puts "#{m.to_s}"
133
+ msg_reply=StompMessage::Message.load_xml(m)
134
+ assert msg_reply.body.split[0]== 'ALIVE', "ping not ok #{msg_reply.body}"
135
+ }
136
+ sleep(3)
137
+ assert msg_reply.command== 'stomp_REPLY', "command wrong"
138
+ send_topic.close_topic
139
+ ss_thread.kill
140
+ end
141
+ def test_statistics_reset
142
+ #note activemq or stomp message bus needs to be running
143
+ args={:topic => '/topic/test'}
144
+ ss=StompMessage::StompStatisticsServer.new(args)
145
+ ss_thread = Thread.new {
146
+ ss.run }
147
+ assert ss.topic=='/topic/test', "topic not set properly"
148
+ msg=StompMessage::Message.new('stomp_REPORT', "body")
149
+ assert msg.command=='stomp_REPORT', "message command not correct"
150
+ puts "creating send topic"
151
+ send_topic=StompMessage::StompSendTopic.new(args)
152
+ puts "send topic NO ack"
153
+ send_topic.send_topic(msg,{ :msisdn => "639999"})
154
+ puts "send topic WITH ack"
155
+ msg_reply = ""
156
+ send_topic.setup_auto_close
157
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m| assert true
158
+ puts "#{m.to_s}"
159
+ msg_reply=StompMessage::Message.load_xml(m)
160
+
161
+ }
162
+ sleep(3)
163
+ assert msg_reply.command== 'stomp_REPLY', "command wrong"
164
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m| assert true
165
+ puts "#{m.to_s}"
166
+ msg_reply=StompMessage::Message.load_xml(m)
167
+
168
+ }
169
+ sleep(3)
170
+ assert msg_reply.command== 'stomp_REPLY', "command wrong"
171
+ response = msg_reply.body.to_s
172
+ assert response.include?("stomp_REPORT: 3"), "Stomp_Report 3 not included"
173
+ assert !response.to_s.include?("stomp_REPORT: 0"), "Stomp_Report: 0 included"
174
+ # reset hour statistics
175
+ msg2=StompMessage::Message.new('stomp_RESET', "hour")
176
+ assert msg2.command=='stomp_RESET', "message command not correct"
177
+ puts "creating send msg2 topic"
178
+ send_topic.send_topic(msg2,{ :msisdn => "639999"})
179
+ send_topic.send_topic_acknowledge(msg,{ :msisdn => "639999"}) { |m| assert true
180
+ puts "#{m.to_s}"
181
+ msg_reply=StompMessage::Message.load_xml(m)
182
+
183
+ }
184
+ sleep(3)
185
+ assert msg_reply.command== 'stomp_REPLY', "command wrong"
186
+ response = msg_reply.body.to_s
187
+ assert response.include?("stomp_REPORT: 1"), "Stomp_Report 0 not included"
188
+ assert response.include?("stomp_RESET: 1"), "Stomp_RESET 1 not included"
189
+ assert !response.to_s.include?("stomp_REPORT: 2"), "Stomp_Report: 2 included"
190
+ ss_thread.kill
191
+ end
192
+
193
+ def test_stomp_parti
194
+ #note activemq or stomp message bus needs to be running
195
+ args={}
196
+ args[:topic] = '/topic/test'
197
+ ss=StompMessage::StompServer.new(args)
198
+ ss_thread = Thread.new {
199
+ ss.run }
200
+ assert ss.topic=='/topic/test', "topic not set properly"
201
+ participant=OpenWFE::StompParticipant.new('/topic/test') { puts "hello from block"}
202
+ wi = OpenWFE::WorkItem.new()
203
+ wi[:command] = 'stomp_PING'
204
+ wi[:body] = 'body'
205
+ wi[:msisdn] = '63999999'
206
+ m2=StompMessage::Message.new(wi[:command].to_s,wi[:body].to_s)
207
+ puts "m2: #{m2.to_xml}"
208
+ assert m2.to_xml==StompMessage::Message.load_xml(m2.to_xml).to_xml, "problems in wi xml"
209
+ wi['sms'] = "hello there"
210
+ participant.consume(wi)
211
+ ss_thread.kill
212
+
23
213
  end
24
214
  end
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: stomp_message
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.3
7
- date: 2007-09-19 00:00:00 +08:00
8
- summary: description of gem
6
+ version: 0.1.2
7
+ date: 2007-10-17 00:00:00 +08:00
8
+ summary: handling stomp messages and servers
9
9
  require_paths:
10
10
  - lib
11
- email: FIXME email
12
- homepage: http://stomp_message.rubyforge.org
13
- rubyforge_project: stomp_message
14
- description: description of gem
11
+ email: scott dot sproule at ficonab dot com
12
+ homepage: http://stompmessage.rubyforge.org
13
+ rubyforge_project: stompmessage
14
+ description: handling stomp messages and servers
15
15
  autorequire:
16
16
  default_executable:
17
17
  bindir: bin
@@ -27,7 +27,7 @@ signing_key:
27
27
  cert_chain:
28
28
  post_install_message:
29
29
  authors:
30
- - FIXME full name
30
+ - scott sproule
31
31
  files:
32
32
  - History.txt
33
33
  - License.txt
@@ -38,6 +38,10 @@ files:
38
38
  - config/requirements.rb
39
39
  - lib/stomp_message.rb
40
40
  - lib/stomp_message/version.rb
41
+ - lib/stomp_message/stomp_server.rb
42
+ - lib/stomp_message/stomp_statistics_server.rb
43
+ - lib/stomp_message/stomp_send_topic.rb
44
+ - lib/stomp_message/stomp_participant.rb
41
45
  - lib/stomp_message/message.rb
42
46
  - log/debug.log
43
47
  - bin/stomp_message_send.rb