emissary 1.3.20 → 1.3.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,148 @@
1
+ #
2
+ # This class represents the INI file and can be used to parse INI files.
3
+ # Derived from IniFile gem, found on http://rubyforge.org/projects/inifile/
4
+ #
5
+ class IniFile
6
+
7
+ #
8
+ # call-seq:
9
+ # IniFile.load( filename )
10
+ # IniFile.load( filename, options )
11
+ #
12
+ # Open the given _filename_ and load the contents of the INI file.
13
+ # The following _options_ can be passed to this method:
14
+ #
15
+ # :comment => ';' The line comment character(s)
16
+ # :parameter => '=' The parameter / value separator
17
+ #
18
+ def self.load( filename, opts = {} )
19
+ if filename
20
+ new(filename, opts)
21
+ else
22
+ nil
23
+ end
24
+ end
25
+
26
+ def initialize( filename, opts = {} )
27
+ @fn = filename
28
+ @comment = opts[:comment] || '#'
29
+ @param = opts[:parameter] || '='
30
+ @ini = Hash.new {|h,k| h[k] = Hash.new}
31
+
32
+ @rgxp_comment = /^\s*$|^\s*[#{@comment}]/
33
+ @rgxp_section = /^\s*\[([^\]]+)\]/
34
+ @rgxp_param = /^([^#{@param}]+)#{@param}(.*)$/
35
+
36
+ parse
37
+ end
38
+
39
+ #
40
+ # call-seq:
41
+ # each {|section, parameter, value| block}
42
+ #
43
+ # Yield each _section_, _parameter_, _value_ in turn to the given
44
+ # _block_. The method returns immediately if no block is supplied.
45
+ #
46
+ def each
47
+ return unless block_given?
48
+ @ini.each do |section,hash|
49
+ hash.each do |param,val|
50
+ yield section, param, val
51
+ end
52
+ end
53
+ self
54
+ end
55
+
56
+ #
57
+ # call-seq:
58
+ # each_section {|section| block}
59
+ #
60
+ # Yield each _section_ in turn to the given _block_. The method returns
61
+ # immediately if no block is supplied.
62
+ #
63
+ def each_section
64
+ return unless block_given?
65
+ @ini.each_key {|section| yield section}
66
+ self
67
+ end
68
+
69
+ #
70
+ # call-seq:
71
+ # ini_file[section]
72
+ #
73
+ # Get the hash of parameter/value pairs for the given _section_.
74
+ #
75
+ def []( section )
76
+ return nil if section.nil?
77
+ @ini[section.to_s]
78
+ end
79
+
80
+ #
81
+ # call-seq:
82
+ # has_section?( section )
83
+ #
84
+ # Returns +true+ if the named _section_ exists in the INI file.
85
+ #
86
+ def has_section?( section )
87
+ @ini.has_key? section.to_s
88
+ end
89
+
90
+ #
91
+ # call-seq:
92
+ # sections
93
+ #
94
+ # Returns an array of the section names.
95
+ #
96
+ def sections
97
+ @ini.keys
98
+ end
99
+
100
+ private
101
+
102
+ def cleanup(str)
103
+ str = str.strip
104
+ first = str[0..0]; last = str[-1..-1]
105
+ str = str[1..-2] if first == last && (first == '"' || first == "'")
106
+ end
107
+ #
108
+ # call-seq
109
+ # parse
110
+ #
111
+ # Parse the ini file contents.
112
+ #
113
+ def parse
114
+ return unless ::Kernel.test ?f, @fn
115
+ section = nil
116
+
117
+ ::File.open(@fn, 'r') do |f|
118
+ while line = f.gets
119
+ line = line.chomp
120
+
121
+ case line
122
+ # ignore blank lines and comment lines
123
+ when @rgxp_comment: next
124
+
125
+ # this is a section declaration
126
+ when @rgxp_section: section = @ini[$1.strip.downcase]
127
+
128
+ # otherwise we have a parameter
129
+ when @rgxp_param
130
+ begin
131
+ val = $2.strip
132
+ val = val[1..-2] if val[0..0] == "'" || val[-1..-1] == '"'
133
+ section[$1.strip.downcase.to_sym] = val
134
+ rescue NoMethodError
135
+ raise "Bad configuration - inifile parameter encountered before first section"
136
+ end
137
+
138
+ else
139
+ raise "Bad configuration -- inifile could not parse line '#{line}"
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+
148
+
@@ -0,0 +1,130 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ #
17
+ # $Id $
18
+ # author: Carl P. corliss
19
+ # Copyright 2009, The New York Times
20
+ #
21
+ require 'syslog'
22
+ require 'fastthread'
23
+
24
+ module Emissary
25
+ class Logger
26
+
27
+ LOG_SYSLOG = 0x01
28
+ LOG_STDOUT = 0x02
29
+ LOG_STDERR = 0x03
30
+
31
+ EMERGENCY = Syslog::Constants::LOG_EMERG
32
+ EMERG = Syslog::Constants::LOG_EMERG
33
+ ALERT = Syslog::Constants::LOG_ALERT
34
+ FATAL = Syslog::Constants::LOG_CRIT
35
+ CRITICAL = Syslog::Constants::LOG_CRIT
36
+ CRIT = Syslog::Constants::LOG_CRIT
37
+ ERROR = Syslog::Constants::LOG_ERR
38
+ ERR = Syslog::Constants::LOG_ERR
39
+ WARNING = Syslog::Constants::LOG_WARNING
40
+ WARN = Syslog::Constants::LOG_WARNING
41
+ NOTICE = Syslog::Constants::LOG_NOTICE
42
+ INFO = Syslog::Constants::LOG_INFO
43
+ DEBUG = Syslog::Constants::LOG_DEBUG
44
+
45
+ CONSTANT_ID_MAP = {
46
+ EMERGENCY => [ :emergency, :emerg ],
47
+ ALERT => [ :alert ],
48
+ CRITICAL => [ :fatal, :critical, :crit ],
49
+ ERROR => [ :error, :err ],
50
+ WARNING => [ :warning, :warn ],
51
+ NOTICE => [ :notice ],
52
+ INFO => [ :info ],
53
+ DEBUG => [ :debug ]
54
+ }
55
+
56
+ CONSTANT_NAME_MAP = CONSTANT_ID_MAP.inject({}) do |mappings,(id,names)|
57
+ names.each { |name| mappings.merge!({ name => id, name.to_s.upcase => id, name.to_s.downcase => id }) }
58
+ mappings
59
+ end
60
+
61
+ attr_accessor :mode, :level, :name
62
+
63
+ def self.instance(mode = LOG_STDERR, log_level = NOTICE, name = nil)
64
+ @@logger ||= Emissary::Logger.new mode, log_level
65
+ end
66
+
67
+ private :initialize
68
+
69
+ def initialize mode, log_level, name = nil
70
+ @mode = mode || LOG_STDERR
71
+ @level = log_level || INFO
72
+ @name = name
73
+ @mutex = Mutex.new
74
+ end
75
+
76
+ def name=(p) @name = n || File.basename($0); end
77
+
78
+ def normalize log_level
79
+ return log_level unless not log_level.kind_of? Fixnum
80
+ return CONSTANT_NAME_MAP[log_level.to_s.downcase] rescue INFO
81
+ return INFO
82
+ end
83
+
84
+ def level_to_sym
85
+ CONSTANT_ID_MAP[@level].first
86
+ end
87
+
88
+ def level_to_s
89
+ level_to_sym.to_s.upcase
90
+ end
91
+
92
+ def loggable?(log_level)
93
+ log_level <= normalize(@level)
94
+ end
95
+
96
+ def syslogger
97
+ @mutex.synchronize {
98
+ Syslog.open(name, Syslog::LOG_PID | Syslog::LOG_NDELAY, Syslog::LOG_DAEMON) do |s|
99
+ s.LOG_UPTO(level)
100
+ yield s
101
+ end
102
+ }
103
+ end
104
+
105
+ def log(log_level, message, *args)
106
+ case @mode
107
+ when Logger::LOG_SYSLOG
108
+ messages = "#{CONSTANT_ID_MAP[log_level].first.to_s.upcase}: #{message}".gsub(/\t/, ' '*8).split(/\n/)
109
+ # some syslog daemons have a hard time with newlines, so split into multiple lines
110
+ messages.each do |message|
111
+ syslogger { |s| s.log(log_level, message, *args) }
112
+ end
113
+ when Logger::LOG_STDOUT
114
+ $stdout << sprintf(message << "\n", *args) unless not loggable? log_level
115
+ when Logger::LOG_STDERR
116
+ $stderr << sprintf(message << "\n", *args) unless not loggable? log_level
117
+ end
118
+
119
+ self
120
+ end
121
+
122
+ CONSTANT_ID_MAP.values.flatten.each do |log_level|
123
+ class_eval %(
124
+ def #{log_level}(message, *args)
125
+ log(#{'::Emissary::Logger::' + log_level.to_s.upcase}, message, *args)
126
+ end
127
+ )
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,217 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'uuid'
17
+ require 'bert'
18
+
19
+ module Emissary
20
+ class Message
21
+
22
+ attr_accessor :sender, :recipient, :replyto, :errors
23
+ attr_accessor :status, :operation, :thread, :time
24
+ attr_accessor :account, :agent, :method, :args
25
+ attr_reader :uuid, :originator
26
+
27
+ def initialize(payload = {})
28
+ @recipient = ''
29
+ @sender = Emissary.identity.queue_name
30
+ @replyto = Emissary.identity.queue_name
31
+ @originator = @sender.dup
32
+
33
+ @status = [ :ok, '' ] # tuple of (status type, status message)
34
+ @operation = -1
35
+ @thread = -1
36
+ @uuid = UUID.new.generate
37
+
38
+ @errors = []
39
+
40
+ @agent = @method = nil
41
+ @account = Emissary.identity.account_id
42
+ @args = []
43
+
44
+ @time = {
45
+ :received => nil,
46
+ :sent => nil,
47
+ }
48
+
49
+ payload = {
50
+ :headers => {
51
+ :recipient => @recipient,
52
+ :sender => @sender,
53
+ :replyto => @replyto,
54
+ :originator => @originator,
55
+ :status => @status,
56
+ :operation => @operation,
57
+ :thread => @thread,
58
+ :uuid => @uuid,
59
+ :time => @time.merge((payload[:time].symbolize rescue {})),
60
+ }.merge((payload[:headers].symbolize rescue {})),
61
+ :data => {
62
+ :account => @account,
63
+ :agent => @agent,
64
+ :method => @method,
65
+ :args => @args
66
+ }.merge((payload[:data].symbolize! rescue {})),
67
+ :errors => [ ] + (payload[:errors].symbolize rescue [])
68
+ }
69
+
70
+ payload[:headers].merge(payload[:data]).each do |k,v|
71
+ send("#{k}=".to_sym, v) rescue nil
72
+ end
73
+
74
+ payload[:errors].each do |e|
75
+ exception = ::Emissary.klass_const(e[:type]).new(e[:message]) rescue StandardError.new("#{e[:type]}: #{e[:message]}")
76
+ exception.set_backtrace(e[:backtrace])
77
+ errors << exception
78
+ end
79
+
80
+ @agent = @agent.to_sym rescue nil
81
+ @method = @method.to_sym rescue nil
82
+ @args = @args || [] rescue []
83
+ end
84
+
85
+ def headers()
86
+ {
87
+ :recipient => recipient,
88
+ :sender => sender,
89
+ :replyto => replyto,
90
+ :originator => originator,
91
+ :status => status,
92
+ :operation => operation,
93
+ :thread => thread,
94
+ :time => time,
95
+ :uuid => uuid
96
+ }
97
+ end
98
+
99
+ def data()
100
+ return { :account => account, :agent => agent, :method => method, :args => args }
101
+ end
102
+
103
+ def errors type = :default
104
+ case type
105
+ when :hashes
106
+ @errors.collect do |e|
107
+ { :type => e.class.name, :message => e.message, :backtrace => e.backtrace }
108
+ end
109
+ else
110
+ @errors
111
+ end
112
+ end
113
+
114
+ def status_type=(t) status[0] = t.to_sym; end
115
+ def status_type() status[0] || :ok rescue :ok; end
116
+
117
+ def status_note=(n) status[1] = n; end
118
+ def status_note() status[1] || '' rescue '' ; end
119
+
120
+ def route who = :recipient
121
+ headers[who].split(':') || [] rescue []
122
+ end
123
+
124
+ def routing_key who = :recipient
125
+ route(who)[0] || nil rescue nil
126
+ end
127
+
128
+ def exchange who = :recipient
129
+ [ exchange_type(who), exchange_name(who) ]
130
+ end
131
+
132
+ def exchange_type who = :recipient
133
+ route(who)[1].to_sym || :direct rescue :direct
134
+ end
135
+
136
+ def exchange_name who = :recipient
137
+ key, type, name = route(who) rescue [ nil, :direct, 'amq.direct' ]
138
+ name || case type.to_sym
139
+ when :fanout, :topic, :matches, :headers
140
+ "amq.#{type}"
141
+ else
142
+ 'amq.direct'
143
+ end
144
+ rescue
145
+ 'amq.direct'
146
+ end
147
+
148
+ def canonical_route who = :recipient
149
+ "#{routing_key(who)}:#{exchange(who).join(':')}"
150
+ end
151
+
152
+ def will_loop?
153
+ canonical_route(:recipient) == canonical_route(:originator)
154
+ end
155
+
156
+ def encode
157
+ BERT.encode({ :headers => headers, :data => data, :errors => errors(:hashes) })
158
+ end
159
+
160
+ def self.decode payload
161
+ begin
162
+ self.new BERT.decode(payload)
163
+ rescue StandardError => e
164
+ raise e unless e.message =~ /bad magic/i
165
+ Emissary.logger.error "Unable to decode message - maybe it wasn't encoded with BERT..?"
166
+ raise ::Emissary::Error::InvalidMessageFormat, "Unable to decode message - maybe it wasn't encoded with BERT? Message: #{payload.inspect}"
167
+ end
168
+ end
169
+
170
+ def stamp_sent!
171
+ time[:sent] = Time.now.to_f
172
+ self
173
+ end
174
+
175
+ def stamp_received!
176
+ time[:received] = Time.now.to_f
177
+ self
178
+ end
179
+
180
+ def trip_time
181
+ (time[:sent].to_f - time[:received].to_f rescue 0.0) || 0.0
182
+ end
183
+
184
+ def response
185
+ header = {
186
+ :recipient => replyto || sender,
187
+ :status => [ :ok, '' ],
188
+ :operation => operation,
189
+ :thread => thread,
190
+ :time => {
191
+ :received => nil,
192
+ :sent => nil
193
+ }
194
+ }
195
+ return Message.new({ :headers => header, :data => data, :errors => errors(:hashes) })
196
+ end
197
+
198
+ def bounce(message = nil)
199
+ message ||= 'Message failed due to missing handler.'
200
+ bounced = self.response
201
+ bounced.status = [ :bounced, message ]
202
+ return bounced
203
+ end
204
+
205
+ def error(message = nil)
206
+ message ||= 'Message failed due to unspecified error.'
207
+ error = self.response
208
+ error.status = [ :errored, message.to_s ]
209
+ if message.is_a? Exception
210
+ error.errors << message
211
+ else
212
+ ::Emissary.logger.warning "#{message.class.name} is not an exception..."
213
+ end
214
+ return error
215
+ end
216
+ end
217
+ end