rapns 3.3.2-java → 3.4.0-java

Sign up to get free protection for your applications and to get access to all the features.
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."]