thrift-amqp-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 47b60f6d88a19c39352bd0173213c238299e01c6
4
+ data.tar.gz: 96733bf92ae83b43a9da73b7a449f6c33c7e6aff
5
+ SHA512:
6
+ metadata.gz: 6b76ebc19ab8fe491c572f6d95fca291ea1f7592474c596ae7e3dcbf58b74b378ff766a0a7d9fa8fb8eace1fe6389a46ce75b8bf47b761a6ce191475b5241742
7
+ data.tar.gz: 651f7479ecc599907ded1610d25c8087281552ab747cb72601a637128c88f037b861eb83876e55d334e8fd582fe194869fc1a2ea4f2855bbd816f7a59ec84536
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in thrift-amqp-ruby.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Alexis Montagne
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Thrift::Amqp::Ruby
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'thrift-amqp-ruby'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install thrift-amqp-ruby
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/thrift-amqp-ruby/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,165 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'thrift'
21
+ require 'bunny'
22
+ require 'stringio'
23
+ require 'timeout'
24
+ require 'uuidtools'
25
+
26
+ module Thrift
27
+
28
+ class ResponseTimeout < Timeout::Error; end
29
+
30
+ class AmqpRpcClientTransport < BaseTransport
31
+
32
+ def initialize(service_queue_name, opts={})
33
+ @service_queue_name = service_queue_name
34
+ @outbuf = Bytes.empty_byte_buffer
35
+
36
+ if opts[:connection].nil?
37
+ if opts[:host].nil?
38
+ raise ArgumentError, ":host key not provided in opts dict to make connection"
39
+ end
40
+
41
+ if opts[:port].nil?
42
+ raise ArgumentError, ":port key not provided in opts dict to make connection"
43
+ end
44
+
45
+ vhost = opts[:vhost] || "/"
46
+ user = opts[:user] || "guest"
47
+ password = opts[:password] || "guest"
48
+ ssl = opts[:ssl] || false
49
+
50
+ @conn = Bunny.new(:host => opts[:host], :port => opts[:port], :vhost => vhost, :user => user, :password => password, :ssl=> ssl)
51
+
52
+ @conn.start
53
+ @connection_started = true
54
+ else
55
+ @conn = opts[:connection]
56
+ @connection_started = false
57
+ end
58
+
59
+ @from_name = opts[:from_name].nil? ? "Unknown Client" : opts[:from_name]
60
+ @exchange = opts[:exchange] || nil
61
+
62
+ @ch = @conn.create_channel
63
+ @service_exchange = @exchange.nil? ? @ch.default_exchange : @ch.direct(@exchange, :durable => true)
64
+ @service_response_exchange = @ch.default_exchange
65
+ @reply_queue = @ch.queue("", :exclusive => true)
66
+ @is_opened = true
67
+
68
+ end
69
+
70
+ def close
71
+ if @is_opened
72
+ @reply_queue.delete
73
+ @ch.close
74
+
75
+ if @connection_started
76
+ @conn.close
77
+ @connection_started = false
78
+ end
79
+
80
+ @is_opened = false
81
+ end
82
+ end
83
+
84
+ def open?; @is_opened end
85
+ def read(sz); @inbuf.read sz end
86
+ def write(buf); @outbuf << Bytes.force_binary_encoding(buf) end
87
+
88
+ #If blocking is set to true then wait for a response message in the reply_to queue, otherwise
89
+ #just send and go!
90
+ def flush(options={})
91
+
92
+ operation = options.has_key?(:operation) ? options[:operation] : ""
93
+ blocking = options.has_key?(:blocking) ? options[:blocking] : true
94
+ msg_timeout = options.has_key?(:msg_timeout) ? options[:msg_timeout] : 10
95
+ log_messages = options.has_key?(:log_messages) ? options[:log_messages] : false
96
+
97
+ correlation_id = self.generate_uuid
98
+
99
+ headers = {:service_name => @service_queue_name,
100
+ :operation => operation,
101
+ :response_required => blocking, #Tell the receiver if a response is required
102
+ :from_name => @from_name
103
+ }
104
+
105
+ #Publish the message
106
+ print_log "Publishing message reply-to: #{@reply_queue.name} - headers: #{headers}", correlation_id if log_messages
107
+ start_time = Time.now
108
+ @service_exchange.publish(@outbuf,
109
+ :routing_key => @service_queue_name,
110
+ :correlation_id => correlation_id,
111
+ :expiration => msg_timeout,
112
+ :reply_to => @reply_queue.name,
113
+ :headers => headers)
114
+
115
+ #If this is a standard RPC blocking call, then wait for there to be a response from the
116
+ #service provider or timeout and log the timeout
117
+ if blocking
118
+ @response = ""
119
+ begin
120
+ #Adding 1sec to timeout to account for clock differences
121
+ Timeout.timeout(msg_timeout + 1, ResponseTimeout) do
122
+ @reply_queue.subscribe(:block => true) do |delivery_info, properties, payload|
123
+
124
+ if log_messages
125
+ response_time = Time.now - start_time
126
+ print_log "---- Response Message received in #{response_time}sec for #{@reply_queue.name}", correlation_id
127
+ print_log "HEADERS: #{properties}", correlation_id
128
+ end
129
+
130
+ if properties[:correlation_id] == correlation_id
131
+ @response = payload
132
+
133
+ #once the return message has been received, no need to continue a subscription
134
+ delivery_info.consumer.cancel
135
+ end
136
+ end
137
+ end
138
+ rescue ResponseTimeout => ex
139
+ #Trying to work around weirdness being seen in a multi threaded workflow environment
140
+ if @response == ""
141
+ msg = "A timeout has occurred (#{msg_timeout}sec) trying to call #{@service_queue_name}.#{operation}"
142
+ print_log msg, correlation_id
143
+ raise ex, msg
144
+ else
145
+ print_log "Ignoring timeout - #{@response}", correlation_id
146
+ end
147
+ end
148
+ @inbuf = StringIO.new Bytes.force_binary_encoding(@response)
149
+ end
150
+ @outbuf = Bytes.empty_byte_buffer
151
+ end
152
+
153
+ protected
154
+
155
+ def generate_uuid
156
+ UUIDTools::UUID.timestamp_create.to_s
157
+ end
158
+
159
+ def print_log(message="", correlation_id="")
160
+ puts "#{Time.now.utc} C Thread: #{Thread.current.object_id} CID:#{correlation_id} - #{message}"
161
+ end
162
+ end
163
+ end
164
+
165
+
@@ -0,0 +1,165 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'bunny'
21
+ require 'thrift'
22
+
23
+ module Thrift
24
+ class AmqpRpcServer < BaseServer
25
+
26
+ class ProcessingTimeout < Timeout::Error; end
27
+
28
+ def initialize(processor, opts={})
29
+
30
+ @processor = processor
31
+
32
+ if opts[:connection].nil?
33
+
34
+ if opts[:host].nil?
35
+ raise ArgumentError, ":host key not provided in opts dict to make connection"
36
+ end
37
+
38
+ if opts[:port].nil?
39
+ raise ArgumentError, ":port key not provided in opts dict to make connection"
40
+ end
41
+
42
+ vhost = opts[:vhost] || "/"
43
+ user = opts[:user] || "guest"
44
+ password = opts[:password] || "guest"
45
+ ssl = opts[:ssl] || false
46
+
47
+ @conn = Bunny.new(:host => opts[:host], :port => opts[:port], :vhost => vhost, :user => user, :password => password, :ssl=> ssl)
48
+ @conn.start
49
+ else
50
+ @conn = opts[:connection]
51
+ end
52
+
53
+ #print "service:", @conn, "\n"
54
+
55
+ if not opts.has_key?(:queue_name)
56
+ raise ArgumentError, "A service queue name has not been specified"
57
+ end
58
+
59
+ @queue_name = opts[:queue_name]
60
+ @protocol_factory = opts[:protocol_factory] || BinaryProtocolFactory
61
+ @exchange = opts[:exchange] || nil
62
+
63
+ end
64
+
65
+ def close
66
+
67
+ if not @request_channel.nil? and @request_channel.respond_to?('close')
68
+ @request_channel.close
69
+ end
70
+
71
+ #Always close the broker connection when closing the server
72
+ @conn.close
73
+
74
+ end
75
+
76
+
77
+
78
+ def serve(options={})
79
+ log_messages = options[:log_messages] || false
80
+ max_messages = options[:max_messages].nil? ? 10 : options[:max_messages]
81
+ response_timeout = options[:response_timeout] || 10
82
+
83
+ #Create a channel to the service queue
84
+ @request_channel = @conn.create_channel(nil, max_messages )
85
+
86
+ if @exchange.nil?
87
+ @service_exchange = @request_channel.default_exchange
88
+ @request_queue = @request_channel.queue(@queue_name, :auto_delete => true)
89
+ else
90
+ @service_exchange = @request_channel.direct(@exchange,:durable => true)
91
+ @request_queue = @request_channel.queue(@queue_name, :auto_delete => true).bind(@service_exchange, :routing_key => @queue_name)
92
+ end
93
+
94
+ @request_queue.subscribe(:block => true) do |delivery_info, properties, payload|
95
+
96
+ if log_messages
97
+ Thread.current["correlation_id"] = properties.correlation_id
98
+ print_log "---- Message received ----"
99
+ print_log "HEADERS: #{properties}"
100
+ end
101
+
102
+ Thread.current["correlation_id"] = properties.correlation_id
103
+
104
+ response_channel = @conn.create_channel
105
+ response_exchange = response_channel.default_exchange
106
+
107
+ response_required = properties.headers.has_key?('response_required') ? properties.headers['response_required'] : true
108
+ process_timeout = response_timeout.to_i > properties.expiration.to_i ? response_timeout.to_i : properties.expiration.to_i
109
+
110
+ #Binary content will imply thrift based message payload
111
+ if properties.content_type == 'application/octet-stream'
112
+
113
+ print_log "Request to process #{@queue_name}.#{properties.headers['operation']} in #{process_timeout}sec" if log_messages
114
+
115
+ input = StringIO.new payload
116
+ out = StringIO.new
117
+ transport = IOStreamTransport.new input, out
118
+ protocol = @protocol_factory.new.get_protocol transport
119
+
120
+ begin
121
+ start_time = Time.now
122
+ Timeout.timeout(process_timeout, ProcessingTimeout) do
123
+ @processor.process protocol, protocol
124
+ end
125
+ processing_time = Time.now - start_time
126
+
127
+ #rewind the buffer for reading
128
+ if out.length > 0
129
+ out.rewind
130
+
131
+ print_log "Time to process request: #{processing_time}sec Response length: #{out.length}" if log_messages
132
+
133
+ if response_required
134
+ response_exchange.publish(out.read(out.length),
135
+ :routing_key => properties.reply_to,
136
+ :correlation_id => properties.correlation_id,
137
+ :content_type => 'application/octet-stream' )
138
+ end
139
+ end
140
+
141
+ rescue ProcessingTimeout => ex
142
+ print_log "A timeout has occurred (#{process_timeout}sec) trying to call #{@queue_name}.#{properties.headers['operation']}"
143
+ end
144
+
145
+ else
146
+
147
+ print_log "Unable to process message content of type #{properties.content_type}. The message will be rejected"
148
+ @request_channel.reject(delivery_info.delivery_tag, false)
149
+
150
+ end
151
+
152
+ response_channel.close
153
+
154
+
155
+ end
156
+ end
157
+
158
+ private
159
+
160
+ def print_log(message="")
161
+ puts "#{Time.now.utc} S Thread: #{Thread.current.object_id} CID:#{Thread.current["correlation_id"]} - #{message}"
162
+
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,7 @@
1
+ module Thrift
2
+ module Amqp
3
+ module Ruby
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ require "thrift/amqp/ruby/version"
2
+ require "thrift/amqp/amqp_rpc_client"
3
+ require "thrift/amqp/amqp_rpc_service"
4
+
5
+ module Thrift
6
+ module Amqp
7
+ module Ruby
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'thrift/amqp/ruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "thrift-amqp-ruby"
8
+ spec.version = Thrift::Amqp::Ruby::VERSION
9
+ spec.authors = ["Alexis Montagne"]
10
+ spec.email = ["alexis.montagne@gmail.com"]
11
+ spec.summary = %q{Thrift transport layer over AMQP}
12
+ spec.description = %q{Thrift transport layer over AMQP}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_dependency "bunny"
24
+ spec.add_dependency "thrift"
25
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thrift-amqp-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexis Montagne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bunny
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thrift
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Thrift transport layer over AMQP
70
+ email:
71
+ - alexis.montagne@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - lib/thrift/amqp/amqp_rpc_client.rb
82
+ - lib/thrift/amqp/amqp_rpc_service.rb
83
+ - lib/thrift/amqp/ruby.rb
84
+ - lib/thrift/amqp/ruby/version.rb
85
+ - thrift-amqp-ruby.gemspec
86
+ homepage: ''
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.2.2
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: Thrift transport layer over AMQP
110
+ test_files: []