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
@@ -1,3 +1,10 @@
1
+ ### 3.4.0 (Aug 28, 2013)
2
+ * Rails 4 support.
3
+ * Add apns_certificate_will_expire reflection.
4
+ * Perform storage update in batches where possible, to increase throughput.
5
+ * airbrake_notify is now deprecated, use the Reflection API instead.
6
+ * Fix calling the notification_delivered reflection twice (#149).
7
+
1
8
  ## 3.3.2 (June 30, 2013)
2
9
  * Fix Rails 3.0.x compatibility (#138) (@yoppi).
3
10
  * Ensure Rails does not set a default value for text columns (#137).
data/README.md CHANGED
@@ -4,19 +4,16 @@
4
4
  ### Rapns - Professional grade APNs and GCM for Ruby.
5
5
 
6
6
  * Supports both APNs (iOS) and GCM (Google Cloud Messaging, Android).
7
- * Seamless Rails integration.
8
- * Scalable - choose the number of threads each app spawns.
7
+ * Seamless Rails (3, 4) integration.
8
+ * Scalable - choose the number of persistent connections for each app.
9
9
  * Designed for uptime - signal -HUP to add, update apps.
10
10
  * Stable - reconnects database and network connections when lost.
11
- * Run as a daemon or inside an existing process.
11
+ * Run as a daemon or inside an [existing processs](https://github.com/ileitch/rapns/wiki/Embedding-API).
12
12
  * Use in a scheduler for low-workload deployments ([Push API](https://github.com/ileitch/rapns/wiki/Push-API)).
13
- * Reflection API for fine-grained instrumentation ([Reflection API](https://github.com/ileitch/rapns/wiki/Relfection-API)).
13
+ * Reflection API for fine-grained instrumentation and error handling ([Reflection API](https://github.com/ileitch/rapns/wiki/Reflection-API)).
14
14
  * Works with MRI, JRuby, Rubinius 1.8 and 1.9.
15
- * [Airbrake](http://airbrakeapp.com/) integration.
16
15
  * Built with love.
17
16
 
18
- #### 2.x users please read [upgrading from 2.x to 3.0](https://github.com/ileitch/rapns/wiki/Upgrading-from-version-2.x-to-3.0)
19
-
20
17
  ### Who uses Rapns?
21
18
 
22
19
  [GateGuru](http://gateguruapp.com) and [Desk.com](http://desk.com), among others!
@@ -34,7 +31,7 @@ Generate the migrations, rapns.yml and migrate:
34
31
  rails g rapns
35
32
  rake db:migrate
36
33
 
37
- ## Create an App
34
+ ## Create an App & Notification
38
35
 
39
36
  #### APNs
40
37
 
@@ -44,24 +41,12 @@ If this is your first time using the APNs, you will need to generate SSL certifi
44
41
  app = Rapns::Apns::App.new
45
42
  app.name = "ios_app"
46
43
  app.certificate = File.read("/path/to/sandbox.pem")
47
- app.environment = "sandbox"
44
+ app.environment = "sandbox" # APNs environment.
48
45
  app.password = "certificate password"
49
46
  app.connections = 1
50
47
  app.save!
51
48
  ```
52
49
 
53
- #### GCM
54
- ```ruby
55
- app = Rapns::Gcm::App.new
56
- app.name = "android_app"
57
- app.auth_key = "..."
58
- app.connections = 1
59
- app.save!
60
- ```
61
-
62
- ## Create a Notification
63
-
64
- #### APNs
65
50
  ```ruby
66
51
  n = Rapns::Apns::Notification.new
67
52
  n.app = Rapns::Apns::App.find_by_name("ios_app")
@@ -71,7 +56,18 @@ n.attributes_for_device = {:foo => :bar}
71
56
  n.save!
72
57
  ```
73
58
 
59
+ You should also implement the [apns_certificate_will_expire](https://github.com/ileitch/rapns/wiki/Reflection-API) reflection to monitor when your certificate is due to expire.
60
+
74
61
  #### GCM
62
+
63
+ ```ruby
64
+ app = Rapns::Gcm::App.new
65
+ app.name = "android_app"
66
+ app.auth_key = "..."
67
+ app.connections = 1
68
+ app.save!
69
+ ```
70
+
75
71
  ```ruby
76
72
  n = Rapns::Gcm::Notification.new
77
73
  n.app = Rapns::Gcm::App.find_by_name("android_app")
@@ -80,6 +76,8 @@ n.data = {:message => "hi mom!"}
80
76
  n.save!
81
77
  ```
82
78
 
79
+ GCM also requires you to respond to [Canonical IDs](https://github.com/ileitch/rapns/wiki/Canonical-IDs).
80
+
83
81
  ## Starting Rapns
84
82
 
85
83
  As a daemon:
data/bin/rapns CHANGED
@@ -5,28 +5,29 @@ require 'rapns'
5
5
 
6
6
  environment = ARGV[0]
7
7
 
8
- banner = 'Usage: rapns <Rails environment> [options]'
9
- if environment.nil? || environment =~ /^-/
10
- puts banner
11
- exit 1
12
- end
13
-
14
8
  config = Rapns::ConfigurationWithoutDefaults.new
15
9
 
16
- ARGV.options do |opts|
17
- opts.banner = banner
10
+ options = ARGV.options do |opts|
11
+ opts.banner = 'Usage: rapns <Rails environment> [options]'
18
12
  opts.on('-f', '--foreground', 'Run in the foreground.') { config.foreground = true }
19
- opts.on('-P N', '--db-poll N', Integer, "Frequency in seconds to check for new notifications. Default: #{config.push_poll}.") { |n| config.push_poll = n }
20
- opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback. Default: #{config.feedback_poll}.") { |n| config.feedback_poll = n }
13
+ opts.on('-P N', '--db-poll N', Integer, "Frequency in seconds to check for new notifications.") { |n| config.push_poll = n }
14
+ opts.on('-F N', '--feedback-poll N', Integer, "Frequency in seconds to check for feedback.") { |n| config.feedback_poll = n }
21
15
  opts.on('-e', '--no-error-checks', 'Disable APNs error checking after notification delivery.') { config.check_for_errors = false }
22
16
  opts.on('-n', '--no-airbrake-notify', 'Disables error notifications via Airbrake.') { config.airbrake_notify = false }
23
17
  opts.on('-p PATH', '--pid-file PATH', String, 'Path to write PID file. Relative to Rails root unless absolute.') { |path| config.pid_file = path }
24
- opts.on('-b N', '--batch-size N', Integer, 'ActiveRecord notifications batch size.') { |n| config.batch_size = n }
25
- opts.on('-v', '--version', 'Print this version of rapns.') { puts "rapns #{Rapns::VERSION}"; exit }
18
+ opts.on('-b N', '--batch-size N', Integer, 'Storage backend notification batch size.') { |n| config.batch_size = n }
19
+ opts.on('-B', '--[no-]batch-storage-updates', 'Perform storage updates in batches.') { |v| config.batch_storage_updates = v }
20
+ opts.on('-v', '--version', 'Print the version.') { puts "rapns #{Rapns::VERSION}"; exit }
26
21
  opts.on('-h', '--help', 'You\'re looking at it.') { puts opts; exit }
27
- opts.parse!
28
22
  end
29
23
 
24
+ if environment.nil? || environment =~ /^-/
25
+ puts options.to_s
26
+ exit 1
27
+ end
28
+
29
+ options.parse!
30
+
30
31
  ENV['RAILS_ENV'] = environment
31
32
  load 'config/environment.rb'
32
33
  load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
@@ -11,15 +11,15 @@
11
11
  # Frequency in seconds to check for feedback
12
12
  # config.feedback_poll = 60
13
13
 
14
- # Enable/Disable error notifications via Airbrake.
15
- # config.airbrake_notify = true
16
-
17
14
  # Disable APNs error checking after notification delivery.
18
15
  # config.check_for_errors = true
19
16
 
20
17
  # ActiveRecord notifications batch size.
21
18
  # config.batch_size = 5000
22
19
 
20
+ # Perform updates to the storage backend in batches to reduce IO.
21
+ # config.batch_storage_updates = true
22
+
23
23
  # Path to write PID file. Relative to Rails root unless absolute.
24
24
  # config.pid_file = '/path/to/rapns.pid'
25
25
 
@@ -71,8 +71,12 @@ Rapns.reflect do |on|
71
71
  # on.gcm_canonical_id do |old_id, canonical_id|
72
72
  # end
73
73
 
74
+ # Called when an APNs certificate will expire within 1 month.
75
+ # Implement on.error to catch errors raised when the certificate expires.
76
+ # on.apns_certificate_will_expire do |app, expiration_time|
77
+ # end
78
+
74
79
  # Called when an exception is raised.
75
80
  # on.error do |error|
76
81
  # end
77
-
78
82
  end
@@ -1,6 +1,13 @@
1
1
  require 'active_record'
2
2
  require 'multi_json'
3
3
 
4
+ module Rapns
5
+ def self.attr_accessible_available?
6
+ require 'rails'
7
+ ::Rails::VERSION::STRING < '4'
8
+ end
9
+ end
10
+
4
11
  require 'rapns/version'
5
12
  require 'rapns/deprecation'
6
13
  require 'rapns/deprecatable'
@@ -0,0 +1,3 @@
1
+ * Should internal errors mark a notification as failed?
2
+ * Check cert expiry when connected.
3
+ * Lazy connect on socket write? - would allow to catch initial expiry error and mark on notification.
@@ -3,7 +3,9 @@ module Rapns
3
3
  class Feedback < ActiveRecord::Base
4
4
  self.table_name = 'rapns_feedback'
5
5
 
6
- attr_accessible :device_token, :failed_at, :app
6
+ if Rapns.attr_accessible_available?
7
+ attr_accessible :device_token, :failed_at, :app
8
+ end
7
9
 
8
10
  validates :device_token, :presence => true
9
11
  validates :failed_at, :presence => true
@@ -11,4 +13,4 @@ module Rapns
11
13
  validates_with Rapns::Apns::DeviceTokenFormatValidator
12
14
  end
13
15
  end
14
- end
16
+ end
@@ -2,7 +2,9 @@ module Rapns
2
2
  class App < ActiveRecord::Base
3
3
  self.table_name = 'rapns_apps'
4
4
 
5
- attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key
5
+ if Rapns.attr_accessible_available?
6
+ attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key
7
+ end
6
8
 
7
9
  has_many :notifications, :class_name => 'Rapns::Notification'
8
10
 
@@ -9,7 +9,7 @@ module Rapns
9
9
 
10
10
  CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
11
11
  :airbrake_notify, :check_for_errors, :pid_file, :batch_size,
12
- :push, :store, :logger]
12
+ :push, :store, :logger, :batch_storage_updates]
13
13
 
14
14
  class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
15
15
  end
@@ -31,6 +31,11 @@ module Rapns
31
31
  end
32
32
  end
33
33
 
34
+ def airbrake_notify=(bool)
35
+ Rapns::Deprecation.warn("airbrake_notify is deprecated. Please use the Rapns.reflect API instead.")
36
+ super(bool)
37
+ end
38
+
34
39
  def pid_file=(path)
35
40
  if path && !Pathname.new(path).absolute?
36
41
  super(File.join(Rails.root, path))
@@ -67,6 +72,7 @@ module Rapns
67
72
 
68
73
  self.push_poll = 2
69
74
  self.feedback_poll = 60
75
+ Rapns::Deprecation.muted { self.airbrake_notify = true }
70
76
  self.airbrake_notify = true
71
77
  self.check_for_errors = true
72
78
  self.batch_size = 5000
@@ -74,6 +80,7 @@ module Rapns
74
80
  self.apns_feedback_callback = nil
75
81
  self.store = :active_record
76
82
  self.logger = nil
83
+ self.batch_storage_updates = true
77
84
 
78
85
  # Internal options.
79
86
  self.embedded = false
@@ -9,13 +9,15 @@ require 'rapns/daemon/reflectable'
9
9
  require 'rapns/daemon/interruptible_sleep'
10
10
  require 'rapns/daemon/delivery_error'
11
11
  require 'rapns/daemon/delivery'
12
- require 'rapns/daemon/delivery_queue'
13
12
  require 'rapns/daemon/feeder'
13
+ require 'rapns/daemon/batch'
14
14
  require 'rapns/daemon/app_runner'
15
15
  require 'rapns/daemon/delivery_handler'
16
+ require 'rapns/daemon/delivery_handler_collection'
16
17
 
17
18
  require 'rapns/daemon/apns/delivery'
18
19
  require 'rapns/daemon/apns/disconnection_error'
20
+ require 'rapns/daemon/apns/certificate_expired_error'
19
21
  require 'rapns/daemon/apns/connection'
20
22
  require 'rapns/daemon/apns/app_runner'
21
23
  require 'rapns/daemon/apns/delivery_handler'
@@ -2,9 +2,10 @@ module Rapns
2
2
  module Daemon
3
3
  module Apns
4
4
  class AppRunner < Rapns::Daemon::AppRunner
5
+
5
6
  protected
6
7
 
7
- def started
8
+ def after_start
8
9
  unless Rapns.config.push
9
10
  poll = Rapns.config.feedback_poll
10
11
  @feedback_receiver = FeedbackReceiver.new(app, poll)
@@ -12,7 +13,7 @@ module Rapns
12
13
  end
13
14
  end
14
15
 
15
- def stopped
16
+ def after_stop
16
17
  @feedback_receiver.stop if @feedback_receiver
17
18
  end
18
19
 
@@ -0,0 +1,20 @@
1
+ module Rapns
2
+ module Apns
3
+ class CertificateExpiredError < StandardError
4
+ attr_reader :app, :time
5
+
6
+ def initialize(app, time)
7
+ @app = app
8
+ @time = time
9
+ end
10
+
11
+ def to_s
12
+ message
13
+ end
14
+
15
+ def message
16
+ "#{app.name} certificate expired at #{time}."
17
+ end
18
+ end
19
+ end
20
+ end
@@ -101,6 +101,8 @@ module Rapns
101
101
  end
102
102
 
103
103
  def connect_socket
104
+ check_certificate_expiration
105
+
104
106
  tcp_socket = TCPSocket.new(@host, @port)
105
107
  tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
106
108
  tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -110,6 +112,30 @@ module Rapns
110
112
  Rapns.logger.info("[#{@app.name}] Connected to #{@host}:#{@port}")
111
113
  [tcp_socket, ssl_socket]
112
114
  end
115
+
116
+ def check_certificate_expiration
117
+ cert = @ssl_context.cert
118
+ if certificate_expired?
119
+ Rapns.logger.error(certificate_msg('expired'))
120
+ raise Rapns::Apns::CertificateExpiredError.new(@app, cert.not_after)
121
+ elsif certificate_expires_soon?
122
+ Rapns.logger.warn(certificate_msg('will expire'))
123
+ reflect(:apns_certificate_will_expire, @app, cert.not_after)
124
+ end
125
+ end
126
+
127
+ def certificate_msg(msg)
128
+ time = @ssl_context.cert.not_after.utc.strftime("%Y-%m-%d %H:%M:%S %Z")
129
+ "[#{@app.name}] Certificate #{msg} at #{time}."
130
+ end
131
+
132
+ def certificate_expired?
133
+ @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc
134
+ end
135
+
136
+ def certificate_expires_soon?
137
+ @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc
138
+ end
113
139
  end
114
140
  end
115
141
  end
@@ -16,10 +16,11 @@ module Rapns
16
16
  255 => "None (unknown error)"
17
17
  }
18
18
 
19
- def initialize(app, conneciton, notification)
19
+ def initialize(app, conneciton, notification, batch)
20
20
  @app = app
21
21
  @connection = conneciton
22
22
  @notification = notification
23
+ @batch = batch
23
24
  end
24
25
 
25
26
  def perform
@@ -13,8 +13,8 @@ module Rapns
13
13
  @host, @port = HOSTS[@app.environment.to_sym]
14
14
  end
15
15
 
16
- def deliver(notification)
17
- Rapns::Daemon::Apns::Delivery.perform(@app, connection, notification)
16
+ def deliver(notification, batch)
17
+ Rapns::Daemon::Apns::Delivery.new(@app, connection, notification, batch).perform
18
18
  end
19
19
 
20
20
  def stopped
@@ -1,17 +1,23 @@
1
1
  module Rapns
2
2
  module Daemon
3
3
  class AppRunner
4
+ extend Reflectable
5
+ include Reflectable
6
+
4
7
  class << self
5
8
  attr_reader :runners
6
9
  end
7
10
 
8
11
  @runners = {}
9
12
 
10
- def self.enqueue(notification)
11
- if app = runners[notification.app_id]
12
- app.enqueue(notification)
13
- else
14
- Rapns.logger.error("No such app '#{notification.app_id}' for notification #{notification.id}.")
13
+ def self.enqueue(notifications)
14
+ notifications.group_by(&:app_id).each do |app_id, group|
15
+ batch = Batch.new(group)
16
+ if app = runners[app_id]
17
+ app.enqueue(batch)
18
+ else
19
+ Rapns.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
20
+ end
15
21
  end
16
22
  end
17
23
 
@@ -33,6 +39,7 @@ module Rapns
33
39
  rescue StandardError => e
34
40
  Rapns.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
35
41
  Rapns.logger.error(e)
42
+ reflect(:error, e)
36
43
  end
37
44
  end
38
45
  end
@@ -60,31 +67,36 @@ module Rapns
60
67
  end
61
68
 
62
69
  attr_reader :app
70
+ attr_accessor :batch
63
71
 
64
72
  def initialize(app)
65
73
  @app = app
66
74
  end
67
75
 
68
- def started
69
- end
70
-
71
- def stopped
72
- end
76
+ def before_start; end
77
+ def after_start; end
78
+ def before_stop; end
79
+ def after_stop; end
73
80
 
74
81
  def start
75
- app.connections.times { handlers << start_handler }
76
- started
82
+ before_start
83
+ app.connections.times { handlers.push(start_handler) }
84
+ after_start
77
85
  Rapns.logger.info("[#{app.name}] Started, #{handlers_str}.")
78
86
  end
79
87
 
80
88
  def stop
81
- handlers.map(&:stop)
82
- stopped
83
- handlers.clear
89
+ before_stop
90
+ handlers.stop
91
+ after_stop
84
92
  end
85
93
 
86
- def enqueue(notification)
87
- queue.push(notification)
94
+ def enqueue(batch)
95
+ self.batch = batch
96
+ batch.notifications.each do |notification|
97
+ queue.push([notification, batch])
98
+ reflect(:notification_enqueued, notification)
99
+ end
88
100
  end
89
101
 
90
102
  def sync(app)
@@ -92,20 +104,20 @@ module Rapns
92
104
  diff = handlers.size - app.connections
93
105
  return if diff == 0
94
106
  if diff > 0
95
- diff.times { decrement_handlers }
96
- Rapns.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} remaining.")
107
+ decrement_handlers(diff)
108
+ Rapns.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} running.")
97
109
  else
98
- diff.abs.times { increment_handlers }
99
- Rapns.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} remaining.")
110
+ increment_handlers(diff.abs)
111
+ Rapns.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} running.")
100
112
  end
101
113
  end
102
114
 
103
- def decrement_handlers
104
- handlers.pop.stop
115
+ def decrement_handlers(num)
116
+ num.times { handlers.pop }
105
117
  end
106
118
 
107
- def increment_handlers
108
- handlers << start_handler
119
+ def increment_handlers(num)
120
+ num.times { handlers.push(start_handler) }
109
121
  end
110
122
 
111
123
  def debug
@@ -114,18 +126,28 @@ module Rapns
114
126
  #{@app.name}:
115
127
  handlers: #{num_handlers}
116
128
  queued: #{queue_size}
129
+ batch size: #{batch_size}
130
+ batch processed: #{batch_processed}
117
131
  idle: #{idle?}
118
132
  EOS
119
133
  end
120
134
 
121
135
  def idle?
122
- queue.notifications_processed?
136
+ batch ? batch.complete? : true
123
137
  end
124
138
 
125
139
  def queue_size
126
140
  queue.size
127
141
  end
128
142
 
143
+ def batch_size
144
+ batch ? batch.num_notifications : 0
145
+ end
146
+
147
+ def batch_processed
148
+ batch ? batch.num_processed : 0
149
+ end
150
+
129
151
  def num_handlers
130
152
  handlers.size
131
153
  end
@@ -140,11 +162,11 @@ module Rapns
140
162
  end
141
163
 
142
164
  def queue
143
- @queue ||= Rapns::Daemon::DeliveryQueue.new
165
+ @queue ||= Queue.new
144
166
  end
145
167
 
146
168
  def handlers
147
- @handler ||= []
169
+ @handlers ||= Rapns::Daemon::DeliveryHandlerCollection.new
148
170
  end
149
171
 
150
172
  def handlers_str(count = app.connections)