killbill 3.2.2 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -2
  3. data/Jarfile +7 -7
  4. data/README.md +16 -4
  5. data/VERSION +1 -1
  6. data/gen_config/api.conf +48 -47
  7. data/gen_config/plugin_api.conf +17 -16
  8. data/generators/active_merchant/templates/.gitignore.rb +2 -2
  9. data/generators/active_merchant/templates/lib/api.rb +8 -0
  10. data/generators/active_merchant/templates/plugin.gemspec.rb +1 -0
  11. data/generators/active_merchant/templates/spec/base_plugin_spec.rb +1 -0
  12. data/generators/active_merchant/templates/spec/integration_spec.rb +5 -1
  13. data/killbill.gemspec +1 -0
  14. data/lib/killbill/creator.rb +9 -0
  15. data/lib/killbill/currency.rb +6 -0
  16. data/lib/killbill/gen/api/account_audit_logs_for_object_type.rb +9 -7
  17. data/lib/killbill/gen/api/catalog_user_api.rb +37 -4
  18. data/lib/killbill/gen/api/payment_api.rb +104 -0
  19. data/lib/killbill/gen/api/payment_gateway_api.rb +6 -3
  20. data/lib/killbill/gen/api/plan_phase.rb +1 -8
  21. data/lib/killbill/gen/api/price_list_set.rb +9 -7
  22. data/lib/killbill/gen/api/subscription.rb +20 -1
  23. data/lib/killbill/gen/api/tenant_user_api.rb +3 -3
  24. data/lib/killbill/gen/plugin-api/currency_plugin_with_events_api.rb +207 -0
  25. data/lib/killbill/gen/plugin-api/ext_bus_event.rb +7 -1
  26. data/lib/killbill/gen/plugin-api/invoice_plugin_with_events_api.rb +103 -0
  27. data/lib/killbill/gen/plugin-api/payment_plugin_with_events_api.rb +782 -0
  28. data/lib/killbill/gen/plugin-api/require_gen.rb +3 -0
  29. data/lib/killbill/helpers/active_merchant/configuration.rb +116 -46
  30. data/lib/killbill/helpers/active_merchant/killbill_spec_helper.rb +49 -32
  31. data/lib/killbill/helpers/active_merchant/payment_plugin.rb +18 -6
  32. data/lib/killbill/helpers/active_merchant/private_payment_plugin.rb +3 -3
  33. data/lib/killbill/helpers/active_merchant/utils.rb +37 -70
  34. data/lib/killbill/invoice.rb +6 -0
  35. data/lib/killbill/payment.rb +6 -0
  36. data/lib/killbill/plugin.rb +1 -1
  37. data/lib/killbill/rake_task.rb +1 -1
  38. data/spec/killbill/helpers/configuration_spec.rb +41 -5
  39. data/spec/killbill/helpers/payment_plugin_spec.rb +4 -1
  40. data/spec/killbill/helpers/private_payment_plugin_spec.rb +4 -2
  41. data/spec/killbill/helpers/utils_spec.rb +6 -6
  42. data/spec/killbill/invoice_plugin_api_spec.rb +1 -1
  43. data/spec/killbill/notification_plugin_api_spec.rb +1 -1
  44. data/spec/killbill/payment_plugin_api_spec.rb +1 -1
  45. metadata +19 -3
  46. data/lib/killbill/jnotification.rb +0 -31
@@ -28,10 +28,13 @@
28
28
  require 'killbill/gen/plugin-api/payment_method_info_plugin'
29
29
  require 'killbill/gen/plugin-api/payment_plugin_api'
30
30
  require 'killbill/gen/plugin-api/payment_plugin_api_exception'
31
+ require 'killbill/gen/plugin-api/payment_plugin_with_events_api'
31
32
  require 'killbill/gen/plugin-api/ext_bus_event'
32
33
  require 'killbill/gen/plugin-api/notification_plugin_api'
33
34
  require 'killbill/gen/plugin-api/invoice_plugin_api'
35
+ require 'killbill/gen/plugin-api/invoice_plugin_with_events_api'
34
36
  require 'killbill/gen/plugin-api/currency_plugin_api'
37
+ require 'killbill/gen/plugin-api/currency_plugin_with_events_api'
35
38
  require 'killbill/gen/plugin-api/gateway_notification'
36
39
  require 'killbill/gen/plugin-api/hosted_payment_page_form_descriptor'
37
40
  require 'killbill/gen/plugin-api/payment_transaction_info_plugin'
@@ -1,73 +1,143 @@
1
1
  require 'logger'
2
+ require 'thread_safe'
2
3
 
3
4
  module Killbill
4
5
  module Plugin
5
6
  module ActiveMerchant
6
- mattr_reader :config
7
- mattr_reader :currency_conversions
8
- mattr_reader :gateways
7
+
8
+ mattr_reader :glob_config
9
+ mattr_reader :glob_currency_conversions
10
+
9
11
  mattr_reader :initialized
10
12
  mattr_reader :kb_apis
13
+ mattr_reader :gateway_name
14
+ mattr_reader :gateway_builder
11
15
  mattr_reader :logger
16
+ mattr_reader :config_key_name
17
+ mattr_reader :per_tenant_config_cache
12
18
 
13
- def self.initialize!(gateway_builder, gateway_name, logger, config_file, kb_apis)
14
- @@config = Properties.new(config_file)
15
- @@config.parse!
16
19
 
17
- @@logger = logger
18
- @@logger.log_level = Logger::DEBUG if (@@config[:logger] || {})[:debug]
20
+ class << self
19
21
 
20
- @@currency_conversions = @@config[:currency_conversions]
21
- @@kb_apis = kb_apis
22
+ def initialize!(gateway_builder, gateway_name, logger, config_key_name, config_file, kb_apis)
22
23
 
23
- @@gateways = {}
24
- gateway_configs = @@config[gateway_name.to_sym]
25
- if gateway_configs.is_a?(Array)
26
- default_gateway = nil
27
- gateway_configs.each_with_index do |gateway_config, idx|
28
- gateway_account_id = gateway_config[:account_id]
29
- if gateway_account_id.nil?
30
- @@logger.warn "Skipping config #{gateway_config} -- missing :account_id"
31
- else
32
- @@gateways[gateway_account_id.to_sym] = Gateway.wrap(gateway_builder, logger, gateway_config)
33
- default_gateway = @@gateways[gateway_account_id.to_sym] if idx == 0
24
+ @@logger = logger
25
+ @@kb_apis = kb_apis
26
+ @@gateway_name = gateway_name
27
+ @@gateway_builder = gateway_builder
28
+ @@config_key_name = config_key_name
29
+ @@per_tenant_config_cache = ThreadSafe::Cache.new
30
+
31
+ if defined?(JRUBY_VERSION)
32
+ begin
33
+ # See https://github.com/jruby/activerecord-jdbc-adapter/issues/302
34
+ require 'jdbc/mysql'
35
+ ::Jdbc::MySQL.load_driver(:require) if ::Jdbc::MySQL.respond_to?(:load_driver)
36
+ rescue => e
37
+ @@logger.warn "Unable to load the JDBC driver: #{e}"
34
38
  end
35
39
  end
36
- @@gateways[:default] = default_gateway if @@gateways[:default].nil?
37
- else
38
- @@gateways[:default] = Gateway.wrap(gateway_builder, logger, gateway_configs)
40
+
41
+ initialize_from_global_config!(gateway_builder, gateway_name, logger, config_file)
39
42
  end
40
43
 
41
- if defined?(JRUBY_VERSION)
42
- begin
43
- # See https://github.com/jruby/activerecord-jdbc-adapter/issues/302
44
- require 'jdbc/mysql'
45
- ::Jdbc::MySQL.load_driver(:require) if ::Jdbc::MySQL.respond_to?(:load_driver)
46
- rescue => e
47
- @@logger.warn "Unable to load the JDBC driver: #{e}"
44
+
45
+ def gateways(kb_tenant_id=nil)
46
+ tenant_config = get_tenant_config(kb_tenant_id)
47
+ extract_gateway_config(tenant_config)
48
+ end
49
+
50
+ def currency_conversions(kb_tenant_id=nil)
51
+ tenant_config = get_tenant_config(kb_tenant_id)
52
+ if tenant_config
53
+ tenant_config[:currency_conversions]
54
+ else
55
+ @@glob_currency_conversions
48
56
  end
49
57
  end
50
58
 
51
- begin
52
- require 'active_record'
53
- ::ActiveRecord::Base.establish_connection(@@config[:database])
54
- ::ActiveRecord::Base.logger = @@logger
55
- rescue => e
56
- @@logger.warn "Unable to establish a database connection: #{e}"
59
+ def config(kb_tenant_id=nil)
60
+ get_tenant_config(kb_tenant_id)
57
61
  end
58
62
 
59
- # Configure the ActiveMerchant HTTP backend
60
- connection_type = (@@config[:active_merchant] || {})[:connection_type]
61
- if connection_type == :typhoeus
62
- require 'killbill/ext/active_merchant/typhoeus_connection'
63
+ def converted_currency(currency, kb_tenant_id=nil)
64
+ currency_sym = currency.to_s.upcase.to_sym
65
+ tmp = currency_conversions(kb_tenant_id)
66
+ tmp && tmp[currency_sym]
63
67
  end
64
68
 
65
- @@initialized = true
66
- end
69
+ def invalidate_tenant_config!(kb_tenant_id)
70
+ @@logger.info("Invalidate plugin key #{@@config_key_name}, tenant = #{kb_tenant_id}")
71
+ @@per_tenant_config_cache[kb_tenant_id] = nil
72
+ end
73
+
74
+ private
75
+
76
+ def extract_gateway_config(config)
77
+ gateways_config = {}
78
+ gateway_configs = config[@@gateway_name.to_sym]
79
+ if gateway_configs.is_a?(Array)
80
+ default_gateway = nil
81
+ gateway_configs.each_with_index do |gateway_config, idx|
82
+ gateway_account_id = gateway_config[:account_id]
83
+ if gateway_account_id.nil?
84
+ @@logger.warn "Skipping config #{gateway_config} -- missing :account_id"
85
+ else
86
+ gateways_config[gateway_account_id.to_sym] = Gateway.wrap(gateway_builder, logger, gateway_config)
87
+ default_gateway = gateways_config[gateway_account_id.to_sym] if idx == 0
88
+ end
89
+ end
90
+ gateways_config[:default] = default_gateway if gateways_config[:default].nil?
91
+ else
92
+ gateways_config[:default] = Gateway.wrap(@@gateway_builder, logger, gateway_configs)
93
+ end
94
+ gateways_config
95
+ end
67
96
 
68
- def self.converted_currency(currency)
69
- currency_sym = currency.to_s.upcase.to_sym
70
- @@currency_conversions && @@currency_conversions[currency_sym]
97
+ def get_tenant_config(kb_tenant_id)
98
+
99
+ if @@per_tenant_config_cache[kb_tenant_id].nil?
100
+ # Make the api api to verify if there is a per tenant value
101
+ context = @@kb_apis.create_context(kb_tenant_id) if kb_tenant_id
102
+ values = @@kb_apis.tenant_user_api.get_tenant_values_for_key(@@config_key_name, context) if context
103
+ # If we have a per tenant value, insert it into the cache
104
+ if values && values[0]
105
+ parsed_config = YAML.load(values[0])
106
+ @@per_tenant_config_cache[kb_tenant_id] = parsed_config
107
+ # Otherwise, add global config so we don't have to make the tenant call on each operation
108
+ else
109
+ @@per_tenant_config_cache[kb_tenant_id] = @@glob_config
110
+ end
111
+ end
112
+ # Return value from cache in any case
113
+ @@per_tenant_config_cache[kb_tenant_id]
114
+ end
115
+
116
+ def initialize_from_global_config!(gateway_builder, gateway_name, logger, config_file)
117
+ # Look for global config
118
+ @@glob_config = Properties.new(config_file)
119
+ @@glob_config.parse!
120
+
121
+ @@logger.log_level = Logger::DEBUG if (@@glob_config[:logger] || {})[:debug]
122
+
123
+ @@glob_currency_conversions = @@glob_config[:currency_conversions]
124
+
125
+ begin
126
+ require 'active_record'
127
+ ::ActiveRecord::Base.establish_connection(@@glob_config[:database])
128
+ ::ActiveRecord::Base.logger = @@logger
129
+ rescue => e
130
+ @@logger.warn "Unable to establish a database connection: #{e}"
131
+ end
132
+
133
+ # Configure the ActiveMerchant HTTP backend
134
+ connection_type = (@@glob_config[:active_merchant] || {})[:connection_type]
135
+ if connection_type == :typhoeus
136
+ require 'killbill/ext/active_merchant/typhoeus_connection'
137
+ end
138
+
139
+ @@initialized = true
140
+ end
71
141
  end
72
142
  end
73
143
  end
@@ -13,18 +13,18 @@ module Killbill
13
13
  create_kb_account kb_account_id
14
14
  end
15
15
 
16
- context = @plugin.kb_apis.create_context(kb_tenant_id)
17
- account = @plugin.kb_apis.account_user_api.get_account_by_id(kb_account_id, context)
16
+ context = @plugin.kb_apis.create_context(kb_tenant_id)
17
+ account = @plugin.kb_apis.account_user_api.get_account_by_id(kb_account_id, context)
18
18
 
19
19
  # The rest is pure Ruby
20
- context = context.to_ruby(context)
20
+ context = context.to_ruby(context)
21
21
 
22
22
  # Generate a token
23
23
  pm_properties = build_pm_properties(account, options)
24
24
 
25
- info = Killbill::Plugin::Model::PaymentMethodPlugin.new
25
+ info = Killbill::Plugin::Model::PaymentMethodPlugin.new
26
26
  info.properties = pm_properties
27
- payment_method = @plugin.add_payment_method(kb_account_id, kb_payment_method_id, info, true, properties, context)
27
+ payment_method = @plugin.add_payment_method(kb_account_id, kb_payment_method_id, info, true, properties, context)
28
28
 
29
29
  pm = payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id)
30
30
  pm.should == payment_method
@@ -35,19 +35,19 @@ module Killbill
35
35
  end
36
36
 
37
37
  def build_pm_properties(account = nil, overrides = {})
38
- cc_number = (overrides.delete(:cc_number) || '4242424242424242')
39
- cc_first_name = (overrides.delete(:cc_first_name) || 'John')
40
- cc_last_name = (overrides.delete(:cc_last_name) || 'Doe')
41
- cc_type = (overrides.delete(:cc_type) || 'Visa')
42
- cc_exp_month = (overrides.delete(:cc_exp_month) || 12)
43
- cc_exp_year = (overrides.delete(:cc_exp_year) || 2017)
44
- cc_last_4 = (overrides.delete(:cc_last_4) || 4242)
45
- address1 = (overrides.delete(:address1) || '5, oakriu road')
46
- address2 = (overrides.delete(:address2) || 'apt. 298')
47
- city = (overrides.delete(:city) || 'Gdio Foia')
48
- state = (overrides.delete(:state) || 'FL')
49
- zip = (overrides.delete(:zip) || 49302)
50
- country = (overrides.delete(:country) || 'US')
38
+ cc_number = (overrides.delete(:cc_number) || '4242424242424242')
39
+ cc_first_name = (overrides.delete(:cc_first_name) || 'John')
40
+ cc_last_name = (overrides.delete(:cc_last_name) || 'Doe')
41
+ cc_type = (overrides.delete(:cc_type) || 'Visa')
42
+ cc_exp_month = (overrides.delete(:cc_exp_month) || 12)
43
+ cc_exp_year = (overrides.delete(:cc_exp_year) || 2017)
44
+ cc_last_4 = (overrides.delete(:cc_last_4) || 4242)
45
+ address1 = (overrides.delete(:address1) || '5, oakriu road')
46
+ address2 = (overrides.delete(:address2) || 'apt. 298')
47
+ city = (overrides.delete(:city) || 'Gdio Foia')
48
+ state = (overrides.delete(:state) || 'FL')
49
+ zip = (overrides.delete(:zip) || 49302)
50
+ country = (overrides.delete(:country) || 'US')
51
51
  cc_verification_value = (overrides.delete(:cc_verification_value) || 1234)
52
52
 
53
53
  properties = []
@@ -76,14 +76,14 @@ module Killbill
76
76
 
77
77
  def create_kb_account(kb_account_id)
78
78
  external_key = Time.now.to_i.to_s + '-test'
79
- email = external_key + '@tester.com'
79
+ email = external_key + '@tester.com'
80
80
 
81
- account = ::Killbill::Plugin::Model::Account.new
82
- account.id = kb_account_id
81
+ account = ::Killbill::Plugin::Model::Account.new
82
+ account.id = kb_account_id
83
83
  account.external_key = external_key
84
- account.email = email
85
- account.name = 'Integration spec'
86
- account.currency = :USD
84
+ account.email = email
85
+ account.name = 'Integration spec'
86
+ account.currency = :USD
87
87
 
88
88
  @account_api.accounts << account
89
89
 
@@ -91,8 +91,8 @@ module Killbill
91
91
  end
92
92
 
93
93
  def create_pm_kv_info(key, value)
94
- prop = ::Killbill::Plugin::Model::PluginProperty.new
95
- prop.key = key
94
+ prop = ::Killbill::Plugin::Model::PluginProperty.new
95
+ prop.key = key
96
96
  prop.value = value
97
97
  prop
98
98
  end
@@ -124,17 +124,17 @@ module Killbill
124
124
  def add_payment(kb_payment_id=SecureRandom.uuid, kb_payment_transaction_id=SecureRandom.uuid, kb_payment_transaction_external_key=SecureRandom.uuid, transaction_type=:PURCHASE)
125
125
  kb_payment = get_payment kb_payment_id
126
126
  if kb_payment.nil?
127
- kb_payment = ::Killbill::Plugin::Model::Payment.new
128
- kb_payment.id = kb_payment_id
127
+ kb_payment = ::Killbill::Plugin::Model::Payment.new
128
+ kb_payment.id = kb_payment_id
129
129
  kb_payment.transactions = []
130
130
  @payments << kb_payment
131
131
  end
132
132
 
133
- kb_payment_transaction = ::Killbill::Plugin::Model::PaymentTransaction.new
134
- kb_payment_transaction.id = kb_payment_transaction_id
133
+ kb_payment_transaction = ::Killbill::Plugin::Model::PaymentTransaction.new
134
+ kb_payment_transaction.id = kb_payment_transaction_id
135
135
  kb_payment_transaction.transaction_type = transaction_type
136
- kb_payment_transaction.external_key = kb_payment_transaction_external_key
137
- kb_payment_transaction.created_date = Java::org.joda.time.DateTime.new(Java::org.joda.time.DateTimeZone::UTC)
136
+ kb_payment_transaction.external_key = kb_payment_transaction_external_key
137
+ kb_payment_transaction.created_date = Java::org.joda.time.DateTime.new(Java::org.joda.time.DateTimeZone::UTC)
138
138
  kb_payment.transactions << kb_payment_transaction
139
139
 
140
140
  kb_payment
@@ -144,6 +144,23 @@ module Killbill
144
144
  @payments.find { |payment| payment.id == id.to_s }
145
145
  end
146
146
  end
147
+
148
+ class FakeJavaTenantUserApi
149
+
150
+ attr_accessor :per_tenant_config
151
+
152
+ def initialize(per_tenant_config)
153
+ @per_tenant_config = per_tenant_config
154
+ end
155
+
156
+ def get_tenant_values_for_key(key, context)
157
+ result = @per_tenant_config[context.tenant_id.to_s]
158
+ if result
159
+ return [result]
160
+ end
161
+ nil
162
+ end
163
+ end
147
164
  end
148
165
  end
149
166
  end
@@ -17,14 +17,18 @@ module Killbill
17
17
  @payment_method_model = payment_method_model
18
18
  @transaction_model = transaction_model
19
19
  @response_model = response_model
20
+
20
21
  end
21
22
 
22
23
  def start_plugin
24
+
23
25
  @logger.progname = "#{@identifier.to_s}-plugin"
24
26
 
27
+ @config_key_name = "PLUGIN_CONFIG_#{@plugin_name}".to_sym
25
28
  ::Killbill::Plugin::ActiveMerchant.initialize! @gateway_builder,
26
29
  @identifier.to_sym,
27
30
  @logger,
31
+ @config_key_name,
28
32
  "#{@conf_dir}/#{@identifier.to_s}.yml",
29
33
  @kb_apis
30
34
 
@@ -45,6 +49,14 @@ module Killbill
45
49
  @logger.debug { "after_request: pool.active_connection? = #{pool.active_connection?}, connection.active? = #{connection.active?}, pool.connections.size = #{pool.connections.size}, connections = #{pool.connections.inspect}" }
46
50
  end
47
51
 
52
+
53
+ def on_event(event)
54
+ if (event.event_type == :TENANT_CONFIG_CHANGE || event.event_type == :TENANT_CONFIG_DELETION) &&
55
+ event.meta_data.to_sym == @config_key_name
56
+ ::Killbill::Plugin::ActiveMerchant.invalidate_tenant_config!(event.tenant_id)
57
+ end
58
+ end
59
+
48
60
  def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
49
61
  gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
50
62
  gateway.authorize(amount_in_cents, payment_source, options)
@@ -161,7 +173,7 @@ module Killbill
161
173
 
162
174
  # Go to the gateway
163
175
  payment_processor_account_id = options[:payment_processor_account_id] || :default
164
- gateway = lookup_gateway(payment_processor_account_id)
176
+ gateway = lookup_gateway(payment_processor_account_id, context.tenant_id)
165
177
  gw_response = gateway.store(payment_source, options)
166
178
  response, transaction = save_response_and_transaction(gw_response, :add_payment_method, kb_account_id, context.tenant_id, payment_processor_account_id)
167
179
 
@@ -189,7 +201,7 @@ module Killbill
189
201
 
190
202
  # Delete the card
191
203
  payment_processor_account_id = options[:payment_processor_account_id] || :default
192
- gateway = lookup_gateway(payment_processor_account_id)
204
+ gateway = lookup_gateway(payment_processor_account_id, context.tenant_id)
193
205
  if options[:customer_id]
194
206
  gw_response = gateway.unstore(options[:customer_id], pm.token, options)
195
207
  else
@@ -395,7 +407,7 @@ module Killbill
395
407
  payment_processor_account_ids = options[:payment_processor_account_ids].nil? ? [options[:payment_processor_account_id] || :default] : options[:payment_processor_account_ids].split(',')
396
408
  payment_processor_account_ids.each do |payment_processor_account_id|
397
409
  # Find the gateway
398
- gateway = lookup_gateway(payment_processor_account_id)
410
+ gateway = lookup_gateway(payment_processor_account_id, context.tenant_id)
399
411
 
400
412
  # Filter before each gateway call
401
413
  before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options)
@@ -523,9 +535,9 @@ module Killbill
523
535
  return response, transaction
524
536
  end
525
537
 
526
- def lookup_gateway(payment_processor_account_id=:default)
527
- gateway = ::Killbill::Plugin::ActiveMerchant.gateways[payment_processor_account_id.to_sym]
528
- raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways}" if gateway.nil?
538
+ def lookup_gateway(payment_processor_account_id=:default, kb_tenant_id=nil)
539
+ gateway = ::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)[payment_processor_account_id.to_sym]
540
+ raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, kb_tenant_id = #{kb_tenant_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)}" if gateway.nil?
529
541
  gateway
530
542
  end
531
543
 
@@ -85,9 +85,9 @@ module Killbill
85
85
  ::Killbill::Plugin::ActiveMerchant.kb_apis
86
86
  end
87
87
 
88
- def gateway(payment_processor_account_id=:default)
89
- gateway = ::Killbill::Plugin::ActiveMerchant.gateways[payment_processor_account_id.to_sym]
90
- raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways}" if gateway.nil?
88
+ def gateway(payment_processor_account_id=:default, kb_tenant_id=nil)
89
+ gateway = ::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)[payment_processor_account_id.to_sym]
90
+ raise "Unable to lookup gateway for payment_processor_account_id #{payment_processor_account_id}, , kb_tenant_id = #{kb_tenant_id}, gateways: #{::Killbill::Plugin::ActiveMerchant.gateways(kb_tenant_id)}" if gateway.nil?
91
91
  gateway
92
92
  end
93
93
 
@@ -1,3 +1,5 @@
1
+ require 'thread_safe'
2
+
1
3
  module Killbill
2
4
  module Plugin
3
5
  module ActiveMerchant
@@ -46,100 +48,65 @@ module Killbill
46
48
  end
47
49
  end
48
50
 
49
- # Relies on the fact that hashes enumerate their values in the order that the corresponding keys were inserted (Ruby 1.9+)
50
51
  class BoundedLRUCache
52
+ include ThreadSafe::Util::CheapLockable
51
53
 
52
- def initialize(proc, max_size=10000)
53
- @proc = proc
54
+ def initialize(proc, max_size = 10000)
54
55
  @max_size = max_size
55
-
56
- if defined?(JRUBY_VERSION)
57
- @is_jruby = true
58
- @semaphore = nil
59
-
60
- lru_cache = Class.new(java.util.LinkedHashMap) do
61
- def initialize(max_size)
62
- super(max_size, 1.0, true)
63
- @max_size = max_size
64
- end
65
-
66
- # Note: renaming it to remove_eldest_entry won't work
67
- def removeEldestEntry(eldest)
68
- size > @max_size
69
- end
70
- end.new(@max_size)
71
- @data = java.util.Collections.synchronizedMap(lru_cache)
72
- else
73
- @is_jruby = false
74
- @semaphore = Mutex.new
75
- # TODO Pre-allocate?
76
- @data = {}
56
+ @data = ThreadSafe::Cache.new do |hash, key|
57
+ # mapping key -> value is constant for our purposes
58
+ set_value = hash.fetch_or_store key, value = proc.call(key)
59
+ store_key(key) if value.equal?(set_value) # very same object
60
+ set_value
77
61
  end
62
+ @keys = []
78
63
  end
79
64
 
80
- def [](key)
81
- @is_jruby ? jruby_get(key) : ruby_get(key)
82
- end
65
+ def [](key); @data[key] end
83
66
 
84
67
  def []=(key, val)
85
- @is_jruby ? jruby_set(key, val) : ruby_set(key, val)
68
+ prev_val = @data.get_and_set(key, val)
69
+ prev_val.nil? ? store_key(key) : update_key(key)
70
+ val
86
71
  end
87
72
 
88
- # For testing
73
+ # @private for testing
74
+ def size; @data.size end
89
75
 
90
- def size
91
- @data.size
76
+ # @private for testing
77
+ def keys
78
+ cheap_synchronize { @keys.dup }
92
79
  end
93
80
 
94
- def keys_to_a
95
- @is_jruby ? @data.key_set.to_a : @data.keys
81
+ # @private for testing
82
+ def values
83
+ cheap_synchronize { @keys.map { |key| self[key] } }
96
84
  end
97
85
 
98
- def values_to_a
99
- @is_jruby ? @data.values.to_a : @data.values
100
- end
86
+ protected
101
87
 
102
- private
103
-
104
- def jruby_get(key)
105
- value = @data.get(key)
106
- if value.nil?
107
- value = @proc.call(key)
108
- # Somebody may have beaten us to it but the mapping key -> value is constant for our purposes
109
- jruby_set(key, value)
88
+ def store_key(key)
89
+ cheap_synchronize do
90
+ @keys << key
91
+ remove_eldest_key_if_full
110
92
  end
111
- value
112
93
  end
113
94
 
114
- def jruby_set(key, val)
115
- @data.put(key, val)
116
- end
117
-
118
- def ruby_get(key)
119
- @semaphore.synchronize do
120
- found = true
121
- value = @data.delete(key) { found = false }
122
- if found
123
- @data[key] = value
124
- else
125
- value = @proc.call(key)
126
- @data[key] = value
127
- value
128
- end
95
+ def update_key(key)
96
+ cheap_synchronize do
97
+ @keys.delete(key); @keys << key
98
+ remove_eldest_key_if_full
129
99
  end
130
100
  end
131
101
 
132
- def ruby_set(key, val)
133
- @semaphore.synchronize do
134
- @data.delete(key)
135
- @data[key] = val
136
- if @data.length > @max_size
137
- @data.delete(@data.first[0])
138
- end
139
- val
140
- end
102
+ private
103
+
104
+ def remove_eldest_key_if_full
105
+ @data.delete @keys.shift if @data.size > @max_size
141
106
  end
107
+
142
108
  end
109
+
143
110
  end
144
111
  end
145
112
  end