rapns 3.3.2-java → 3.4.0-java

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.
Files changed (66) hide show
  1. data/CHANGELOG.md +7 -0
  2. data/README.md +19 -21
  3. data/bin/rapns +14 -13
  4. data/lib/generators/templates/rapns.rb +8 -4
  5. data/lib/rapns.rb +7 -0
  6. data/lib/rapns/TODO +3 -0
  7. data/lib/rapns/apns/feedback.rb +4 -2
  8. data/lib/rapns/app.rb +3 -1
  9. data/lib/rapns/configuration.rb +8 -1
  10. data/lib/rapns/daemon.rb +3 -1
  11. data/lib/rapns/daemon/apns/app_runner.rb +3 -2
  12. data/lib/rapns/daemon/apns/certificate_expired_error.rb +20 -0
  13. data/lib/rapns/daemon/apns/connection.rb +26 -0
  14. data/lib/rapns/daemon/apns/delivery.rb +2 -1
  15. data/lib/rapns/daemon/apns/delivery_handler.rb +2 -2
  16. data/lib/rapns/daemon/app_runner.rb +50 -28
  17. data/lib/rapns/daemon/batch.rb +100 -0
  18. data/lib/rapns/daemon/delivery.rb +6 -10
  19. data/lib/rapns/daemon/delivery_handler.rb +14 -12
  20. data/lib/rapns/daemon/delivery_handler_collection.rb +33 -0
  21. data/lib/rapns/daemon/feeder.rb +3 -5
  22. data/lib/rapns/daemon/gcm/delivery.rb +5 -4
  23. data/lib/rapns/daemon/gcm/delivery_handler.rb +2 -2
  24. data/lib/rapns/daemon/store/active_record.rb +23 -2
  25. data/lib/rapns/deprecation.rb +7 -6
  26. data/lib/rapns/logger.rb +1 -1
  27. data/lib/rapns/notification.rb +5 -3
  28. data/lib/rapns/reflection.rb +1 -1
  29. data/lib/rapns/version.rb +1 -1
  30. data/lib/tasks/cane.rake +1 -1
  31. data/lib/tasks/test.rake +8 -3
  32. data/spec/unit/apns/app_spec.rb +4 -4
  33. data/spec/unit/apns_feedback_spec.rb +1 -1
  34. data/spec/unit/configuration_spec.rb +12 -6
  35. data/spec/unit/daemon/apns/app_runner_spec.rb +6 -4
  36. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  37. data/spec/unit/daemon/apns/connection_spec.rb +46 -10
  38. data/spec/unit/daemon/apns/delivery_handler_spec.rb +24 -18
  39. data/spec/unit/daemon/apns/delivery_spec.rb +11 -12
  40. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -16
  41. data/spec/unit/daemon/app_runner_shared.rb +27 -10
  42. data/spec/unit/daemon/app_runner_spec.rb +48 -28
  43. data/spec/unit/daemon/batch_spec.rb +160 -0
  44. data/spec/unit/daemon/delivery_handler_collection_spec.rb +37 -0
  45. data/spec/unit/daemon/delivery_handler_shared.rb +20 -11
  46. data/spec/unit/daemon/feeder_spec.rb +12 -12
  47. data/spec/unit/daemon/gcm/app_runner_spec.rb +4 -2
  48. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +18 -10
  49. data/spec/unit/daemon/gcm/delivery_spec.rb +47 -17
  50. data/spec/unit/daemon/interruptible_sleep_spec.rb +3 -3
  51. data/spec/unit/daemon/reflectable_spec.rb +1 -1
  52. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +1 -1
  53. data/spec/unit/daemon/store/active_record_spec.rb +87 -10
  54. data/spec/unit/daemon_spec.rb +6 -6
  55. data/spec/unit/deprecation_spec.rb +2 -2
  56. data/spec/unit/logger_spec.rb +33 -17
  57. data/spec/unit/notification_shared.rb +7 -3
  58. data/spec/unit/upgraded_spec.rb +8 -14
  59. data/spec/unit_spec_helper.rb +9 -1
  60. metadata +53 -44
  61. data/lib/rapns/daemon/delivery_queue.rb +0 -42
  62. data/lib/rapns/daemon/delivery_queue_18.rb +0 -44
  63. data/lib/rapns/daemon/delivery_queue_19.rb +0 -42
  64. data/spec/acceptance/gcm_upgrade_spec.rb +0 -34
  65. data/spec/acceptance_spec_helper.rb +0 -85
  66. data/spec/unit/daemon/delivery_queue_spec.rb +0 -29
@@ -0,0 +1,100 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Batch
4
+ include Reflectable
5
+
6
+ attr_reader :num_processed, :notifications,
7
+ :delivered, :failed, :retryable
8
+
9
+ def initialize(notifications)
10
+ @notifications = notifications
11
+ @num_processed = 0
12
+ @delivered = []
13
+ @failed = {}
14
+ @retryable = {}
15
+ @mutex = Mutex.new
16
+ end
17
+
18
+ def num_notifications
19
+ @notifications.size
20
+ end
21
+
22
+ def mark_retryable(notification, deliver_after)
23
+ if Rapns.config.batch_storage_updates
24
+ @retryable[deliver_after] ||= []
25
+ @retryable[deliver_after] << notification
26
+ else
27
+ Rapns::Daemon.store.mark_retryable(notification, deliver_after)
28
+ end
29
+ end
30
+
31
+ def mark_delivered(notification)
32
+ if Rapns.config.batch_storage_updates
33
+ @delivered << notification
34
+ else
35
+ Rapns::Daemon.store.mark_delivered(notification)
36
+ end
37
+ end
38
+
39
+ def mark_failed(notification, code, description)
40
+ if Rapns.config.batch_storage_updates
41
+ key = [code, description]
42
+ @failed[key] ||= []
43
+ @failed[key] << notification
44
+ else
45
+ Rapns::Daemon.store.mark_failed(notification, code, description)
46
+ end
47
+ end
48
+
49
+ def notification_processed
50
+ @mutex.synchronize do
51
+ @num_processed += 1
52
+ complete if @num_processed >= @notifications.size
53
+ end
54
+ end
55
+
56
+ def complete?
57
+ @complete == true
58
+ end
59
+
60
+ def describe
61
+ @notifications.map(&:id).join(', ')
62
+ end
63
+
64
+ private
65
+
66
+ def complete
67
+ [:complete_delivered, :complete_failed, :complete_retried].each do |method|
68
+ begin
69
+ send(method)
70
+ rescue StandardError => e
71
+ Rapns.logger.error(e)
72
+ reflect(:error, e)
73
+ end
74
+ end
75
+
76
+ @notifications.clear
77
+ @complete = true
78
+ end
79
+
80
+ def complete_delivered
81
+ Rapns::Daemon.store.mark_batch_delivered(@delivered)
82
+ @delivered.clear
83
+ end
84
+
85
+ def complete_failed
86
+ @failed.each do |(code, description), notifications|
87
+ Rapns::Daemon.store.mark_batch_failed(notifications, code, description)
88
+ end
89
+ @failed.clear
90
+ end
91
+
92
+ def complete_retried
93
+ @retryable.each do |deliver_after, notifications|
94
+ Rapns::Daemon.store.mark_batch_retryable(notifications, deliver_after)
95
+ end
96
+ @retryable.clear
97
+ end
98
+ end
99
+ end
100
+ end
@@ -3,26 +3,22 @@ module Rapns
3
3
  class Delivery
4
4
  include Reflectable
5
5
 
6
- def self.perform(*args)
7
- new(*args).perform
8
- end
9
-
10
- def retry_after(notification, deliver_after)
11
- Rapns::Daemon.store.retry_after(notification, deliver_after)
6
+ def mark_retryable(notification, deliver_after)
7
+ @batch.mark_retryable(notification, deliver_after)
12
8
  reflect(:notification_will_retry, notification)
13
9
  end
14
10
 
15
- def retry_exponentially(notification)
16
- retry_after(notification, Time.now + 2 ** (notification.retries + 1))
11
+ def mark_retryable_exponential(notification)
12
+ mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
17
13
  end
18
14
 
19
15
  def mark_delivered
20
- Rapns::Daemon.store.mark_delivered(@notification)
16
+ @batch.mark_delivered(@notification)
21
17
  reflect(:notification_delivered, @notification)
22
18
  end
23
19
 
24
20
  def mark_failed(code, description)
25
- Rapns::Daemon.store.mark_failed(@notification, code, description)
21
+ @batch.mark_failed(@notification, code, description)
26
22
  reflect(:notification_failed, @notification)
27
23
  end
28
24
  end
@@ -3,6 +3,8 @@ module Rapns
3
3
  class DeliveryHandler
4
4
  include Reflectable
5
5
 
6
+ WAKEUP = :wakeup
7
+
6
8
  attr_accessor :queue
7
9
 
8
10
  def start
@@ -16,10 +18,14 @@ module Rapns
16
18
 
17
19
  def stop
18
20
  @stop = true
19
- if @thread
20
- queue.wakeup(@thread)
21
- @thread.join
22
- end
21
+ end
22
+
23
+ def wakeup
24
+ queue.push(WAKEUP) if @thread
25
+ end
26
+
27
+ def wait
28
+ @thread.join if @thread
23
29
  stopped
24
30
  end
25
31
 
@@ -29,20 +35,16 @@ module Rapns
29
35
  end
30
36
 
31
37
  def handle_next_notification
32
- begin
33
- notification = queue.pop
34
- rescue DeliveryQueue::WakeupError
35
- return
36
- end
38
+ notification, batch = queue.pop
39
+ return if notification == WAKEUP
37
40
 
38
41
  begin
39
- deliver(notification)
40
- reflect(:notification_delivered, notification)
42
+ deliver(notification, batch)
41
43
  rescue StandardError => e
42
44
  Rapns.logger.error(e)
43
45
  reflect(:error, e)
44
46
  ensure
45
- queue.notification_processed
47
+ batch.notification_processed
46
48
  end
47
49
  end
48
50
  end
@@ -0,0 +1,33 @@
1
+ module Rapns
2
+ module Daemon
3
+ class DeliveryHandlerCollection
4
+ attr_reader :handlers
5
+
6
+ def initialize
7
+ @handlers = []
8
+ end
9
+
10
+ def push(handler)
11
+ @handlers << handler
12
+ end
13
+
14
+ def pop
15
+ handler = @handlers.pop
16
+ handler.stop
17
+ handler.wakeup
18
+ @handlers.map(&:wakeup)
19
+ handler.wait
20
+ end
21
+
22
+ def size
23
+ @handlers.size
24
+ end
25
+
26
+ def stop
27
+ @handlers.map(&:stop)
28
+ @handlers.map(&:wakeup)
29
+ @handlers.map(&:wait)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -38,11 +38,9 @@ module Rapns
38
38
  def self.enqueue_notifications
39
39
  begin
40
40
  idle = Rapns::Daemon::AppRunner.idle.map(&:app)
41
-
42
- Rapns::Daemon.store.deliverable_notifications(idle).each do |notification|
43
- Rapns::Daemon::AppRunner.enqueue(notification)
44
- reflect(:notification_enqueued, notification)
45
- end
41
+ return if idle.empty?
42
+ notifications = Rapns::Daemon.store.deliverable_notifications(idle)
43
+ Rapns::Daemon::AppRunner.enqueue(notifications)
46
44
  rescue StandardError => e
47
45
  Rapns.logger.error(e)
48
46
  reflect(:error, e)
@@ -8,10 +8,11 @@ module Rapns
8
8
  GCM_URI = URI.parse('https://android.googleapis.com/gcm/send')
9
9
  UNAVAILABLE_STATES = ['Unavailable', 'InternalServerError']
10
10
 
11
- def initialize(app, http, notification)
11
+ def initialize(app, http, notification, batch)
12
12
  @app = app
13
13
  @http = http
14
14
  @notification = notification
15
+ @batch = batch
15
16
  end
16
17
 
17
18
  def perform
@@ -121,7 +122,7 @@ module Rapns
121
122
 
122
123
  def deliver_after_header(response)
123
124
  if response.header['retry-after']
124
- retry_after = if response.header['retry-after'].to_s =~ /^[0-9]+$/
125
+ if response.header['retry-after'].to_s =~ /^[0-9]+$/
125
126
  Time.now + response.header['retry-after'].to_i
126
127
  else
127
128
  Time.httpdate(response.header['retry-after'])
@@ -131,9 +132,9 @@ module Rapns
131
132
 
132
133
  def retry_delivery(notification, response)
133
134
  if time = deliver_after_header(response)
134
- retry_after(notification, time)
135
+ mark_retryable(notification, time)
135
136
  else
136
- retry_exponentially(notification)
137
+ mark_retryable_exponential(notification)
137
138
  end
138
139
  end
139
140
 
@@ -7,8 +7,8 @@ module Rapns
7
7
  @http = Net::HTTP::Persistent.new('rapns')
8
8
  end
9
9
 
10
- def deliver(notification)
11
- Rapns::Daemon::Gcm::Delivery.perform(@app, @http, notification)
10
+ def deliver(notification, batch)
11
+ Rapns::Daemon::Gcm::Delivery.new(@app, @http, notification, batch).perform
12
12
  end
13
13
 
14
14
  def stopped
@@ -13,11 +13,11 @@ module Rapns
13
13
  batch_size = Rapns.config.batch_size
14
14
  relation = Rapns::Notification.ready_for_delivery.for_apps(apps)
15
15
  relation = relation.limit(batch_size) unless Rapns.config.push
16
- relation.all
16
+ relation.to_a
17
17
  end
18
18
  end
19
19
 
20
- def retry_after(notification, deliver_after)
20
+ def mark_retryable(notification, deliver_after)
21
21
  with_database_reconnect_and_retry do
22
22
  notification.retries += 1
23
23
  notification.deliver_after = deliver_after
@@ -25,6 +25,13 @@ module Rapns
25
25
  end
26
26
  end
27
27
 
28
+ def mark_batch_retryable(notifications, deliver_after)
29
+ ids = notifications.map(&:id)
30
+ with_database_reconnect_and_retry do
31
+ Rapns::Notification.where(:id => ids).update_all(['retries = retries + 1, deliver_after = ?', deliver_after])
32
+ end
33
+ end
34
+
28
35
  def mark_delivered(notification)
29
36
  with_database_reconnect_and_retry do
30
37
  notification.delivered = true
@@ -33,6 +40,13 @@ module Rapns
33
40
  end
34
41
  end
35
42
 
43
+ def mark_batch_delivered(notifications)
44
+ ids = notifications.map(&:id)
45
+ with_database_reconnect_and_retry do
46
+ Rapns::Notification.where(:id => ids).update_all(['delivered = true, delivered_at = ?', Time.now])
47
+ end
48
+ end
49
+
36
50
  def mark_failed(notification, code, description)
37
51
  with_database_reconnect_and_retry do
38
52
  notification.delivered = false
@@ -45,6 +59,13 @@ module Rapns
45
59
  end
46
60
  end
47
61
 
62
+ def mark_batch_failed(notifications, code, description)
63
+ ids = notifications.map(&:id)
64
+ with_database_reconnect_and_retry do
65
+ Rapns::Notification.where(:id => ids).update_all(['delivered = false, delivered_at = NULL, failed = true, failed_at = ?, error_code = ?, error_description = ?', Time.now, code, description])
66
+ end
67
+ end
68
+
48
69
  def create_apns_feedback(failed_at, device_token, app)
49
70
  with_database_reconnect_and_retry do
50
71
  Rapns::Apns::Feedback.create!(:failed_at => failed_at,
@@ -1,20 +1,21 @@
1
1
  module Rapns
2
2
  class Deprecation
3
- def self.silenced
3
+ def self.muted
4
4
  begin
5
- Thread.current[:rapns_silence_deprecations] = true
5
+ orig_val = Thread.current[:rapns_mute_deprecations]
6
+ Thread.current[:rapns_mute_deprecations] = true
6
7
  yield
7
8
  ensure
8
- Thread.current[:rapns_silence_deprecations] = false
9
+ Thread.current[:rapns_mute_deprecations] = orig_val
9
10
  end
10
11
  end
11
12
 
12
- def self.silenced?
13
- Thread.current[:rapns_silence_deprecations]
13
+ def self.muted?
14
+ Thread.current[:rapns_mute_deprecations] == true
14
15
  end
15
16
 
16
17
  def self.warn(msg)
17
- unless Rapns::Deprecation.silenced?
18
+ unless Rapns::Deprecation.muted?
18
19
  STDERR.puts "DEPRECATION WARNING: #{msg}"
19
20
  end
20
21
  end
@@ -32,7 +32,7 @@ module Rapns
32
32
  def setup_logger(log)
33
33
  if Rapns.config.logger
34
34
  @logger = Rapns.config.logger
35
- elsif defined?(ActiveSupport::BufferedLogger)
35
+ elsif ActiveSupport.const_defined?('BufferedLogger')
36
36
  @logger = ActiveSupport::BufferedLogger.new(log, Rails.logger.level)
37
37
  @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
38
38
  else
@@ -9,9 +9,11 @@ module Rapns
9
9
 
10
10
  belongs_to :app, :class_name => 'Rapns::App'
11
11
 
12
- attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry,:delivered,
13
- :delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
14
- :alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids
12
+ if Rapns.attr_accessible_available?
13
+ attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry,:delivered,
14
+ :delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
15
+ :alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids
16
+ end
15
17
 
16
18
  validates :expiry, :numericality => true, :allow_nil => true
17
19
  validates :app, :presence => true
@@ -13,7 +13,7 @@ module Rapns
13
13
  REFLECTIONS = [
14
14
  :apns_feedback, :notification_enqueued, :notification_delivered,
15
15
  :notification_failed, :notification_will_retry, :apns_connection_lost,
16
- :gcm_canonical_id, :error
16
+ :gcm_canonical_id, :error, :apns_certificate_will_expire
17
17
  ]
18
18
 
19
19
  REFLECTIONS.each do |reflection|
@@ -1,3 +1,3 @@
1
1
  module Rapns
2
- VERSION = '3.3.2'
2
+ VERSION = '3.4.0'
3
3
  end
@@ -3,7 +3,7 @@ begin
3
3
 
4
4
  desc "Run cane to check quality metrics"
5
5
  Cane::RakeTask.new(:quality) do |cane|
6
- cane.add_threshold 'coverage/covered_percent', :>=, 99
6
+ cane.add_threshold 'coverage/covered_percent', :>=, 98
7
7
  cane.no_style = false
8
8
  cane.style_measure = 1000
9
9
  cane.no_doc = true
@@ -2,9 +2,13 @@ namespace :test do
2
2
  task :build_rails do
3
3
  require 'fileutils'
4
4
 
5
- def cmd(str)
5
+ def cmd(str, clean_env = true)
6
6
  puts "* #{str}"
7
- retval = Bundler.with_clean_env { `#{str}` }
7
+ retval = if clean_env
8
+ Bundler.with_clean_env { `#{str}` }
9
+ else
10
+ `#{str}`
11
+ end
8
12
  puts retval.strip
9
13
  retval
10
14
  end
@@ -15,7 +19,8 @@ namespace :test do
15
19
  FileUtils.mkdir_p(path)
16
20
  pwd = Dir.pwd
17
21
 
18
- cmd("bundle exec rails new #{path} --skip-bundle")
22
+ cmd("bundle exec rails --version", false)
23
+ cmd("bundle exec rails new #{path} --skip-bundle", false)
19
24
 
20
25
  begin
21
26
  Dir.chdir(path)
@@ -2,26 +2,26 @@ require 'unit_spec_helper'
2
2
 
3
3
  describe Rapns::App do
4
4
  it 'does not validate an app with an invalid certificate' do
5
- app = Rapns::Apns::App.new(:key => 'test', :environment => 'development', :certificate => 'foo')
5
+ app = Rapns::Apns::App.new(:name => 'test', :environment => 'development', :certificate => 'foo')
6
6
  app.valid?
7
7
  app.errors[:certificate].should == ['Certificate value must contain a certificate and a private key.']
8
8
  end
9
9
 
10
10
  it 'validates a certificate without a password' do
11
- app = Rapns::Apns::App.new :key => 'test', :environment => 'development', :certificate => TEST_CERT
11
+ app = Rapns::Apns::App.new :name => 'test', :environment => 'development', :certificate => TEST_CERT
12
12
  app.valid?
13
13
  app.errors[:certificate].should == []
14
14
  end
15
15
 
16
16
  it 'validates a certificate with a password' do
17
- app = Rapns::Apns::App.new :key => 'test', :environment => 'development',
17
+ app = Rapns::Apns::App.new :name => 'test', :environment => 'development',
18
18
  :certificate => TEST_CERT_WITH_PASSWORD, :password => 'fubar'
19
19
  app.valid?
20
20
  app.errors[:certificate].should == []
21
21
  end
22
22
 
23
23
  it 'validates a certificate with an incorrect password' do
24
- app = Rapns::Apns::App.new :key => 'test', :environment => 'development',
24
+ app = Rapns::Apns::App.new :name => 'test', :environment => 'development',
25
25
  :certificate => TEST_CERT_WITH_PASSWORD, :password => 'incorrect'
26
26
  app.valid?
27
27
  app.errors[:certificate].should == ["Certificate value must contain a certificate and a private key."]