rpush 1.0.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 (145) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +99 -0
  3. data/LICENSE +7 -0
  4. data/README.md +189 -0
  5. data/bin/rpush +36 -0
  6. data/config/database.yml +44 -0
  7. data/lib/generators/rpush_generator.rb +44 -0
  8. data/lib/generators/templates/add_adm.rb +23 -0
  9. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  10. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  11. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
  12. data/lib/generators/templates/add_gcm.rb +102 -0
  13. data/lib/generators/templates/add_rpush.rb +349 -0
  14. data/lib/generators/templates/add_wpns.rb +16 -0
  15. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  16. data/lib/generators/templates/create_rapns_feedback.rb +18 -0
  17. data/lib/generators/templates/create_rapns_notifications.rb +29 -0
  18. data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
  19. data/lib/generators/templates/rpush.rb +104 -0
  20. data/lib/rpush/TODO +3 -0
  21. data/lib/rpush/adm/app.rb +15 -0
  22. data/lib/rpush/adm/data_validator.rb +11 -0
  23. data/lib/rpush/adm/notification.rb +29 -0
  24. data/lib/rpush/apns/app.rb +29 -0
  25. data/lib/rpush/apns/binary_notification_validator.rb +12 -0
  26. data/lib/rpush/apns/device_token_format_validator.rb +12 -0
  27. data/lib/rpush/apns/feedback.rb +16 -0
  28. data/lib/rpush/apns/notification.rb +84 -0
  29. data/lib/rpush/apns_feedback.rb +13 -0
  30. data/lib/rpush/app.rb +18 -0
  31. data/lib/rpush/configuration.rb +75 -0
  32. data/lib/rpush/daemon/adm/delivery.rb +222 -0
  33. data/lib/rpush/daemon/adm.rb +9 -0
  34. data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
  35. data/lib/rpush/daemon/apns/delivery.rb +64 -0
  36. data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
  37. data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
  38. data/lib/rpush/daemon/apns.rb +16 -0
  39. data/lib/rpush/daemon/app_runner.rb +187 -0
  40. data/lib/rpush/daemon/batch.rb +115 -0
  41. data/lib/rpush/daemon/constants.rb +59 -0
  42. data/lib/rpush/daemon/delivery.rb +28 -0
  43. data/lib/rpush/daemon/delivery_error.rb +19 -0
  44. data/lib/rpush/daemon/dispatcher/http.rb +21 -0
  45. data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
  46. data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
  47. data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
  48. data/lib/rpush/daemon/feeder.rb +68 -0
  49. data/lib/rpush/daemon/gcm/delivery.rb +222 -0
  50. data/lib/rpush/daemon/gcm.rb +9 -0
  51. data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
  52. data/lib/rpush/daemon/loggable.rb +31 -0
  53. data/lib/rpush/daemon/reflectable.rb +13 -0
  54. data/lib/rpush/daemon/retry_header_parser.rb +23 -0
  55. data/lib/rpush/daemon/retryable_error.rb +20 -0
  56. data/lib/rpush/daemon/service_config_methods.rb +33 -0
  57. data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
  58. data/lib/rpush/daemon/store/active_record.rb +154 -0
  59. data/lib/rpush/daemon/tcp_connection.rb +143 -0
  60. data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
  61. data/lib/rpush/daemon/wpns/delivery.rb +132 -0
  62. data/lib/rpush/daemon/wpns.rb +9 -0
  63. data/lib/rpush/daemon.rb +140 -0
  64. data/lib/rpush/deprecatable.rb +23 -0
  65. data/lib/rpush/deprecation.rb +23 -0
  66. data/lib/rpush/embed.rb +28 -0
  67. data/lib/rpush/gcm/app.rb +11 -0
  68. data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  69. data/lib/rpush/gcm/notification.rb +30 -0
  70. data/lib/rpush/logger.rb +63 -0
  71. data/lib/rpush/multi_json_helper.rb +16 -0
  72. data/lib/rpush/notification.rb +69 -0
  73. data/lib/rpush/notifier.rb +52 -0
  74. data/lib/rpush/payload_data_size_validator.rb +10 -0
  75. data/lib/rpush/push.rb +16 -0
  76. data/lib/rpush/railtie.rb +11 -0
  77. data/lib/rpush/reflection.rb +58 -0
  78. data/lib/rpush/registration_ids_count_validator.rb +10 -0
  79. data/lib/rpush/version.rb +3 -0
  80. data/lib/rpush/wpns/app.rb +9 -0
  81. data/lib/rpush/wpns/notification.rb +26 -0
  82. data/lib/rpush.rb +62 -0
  83. data/lib/tasks/cane.rake +18 -0
  84. data/lib/tasks/rpush.rake +16 -0
  85. data/lib/tasks/test.rake +38 -0
  86. data/spec/functional/adm_spec.rb +43 -0
  87. data/spec/functional/apns_spec.rb +58 -0
  88. data/spec/functional/embed_spec.rb +49 -0
  89. data/spec/functional/gcm_spec.rb +42 -0
  90. data/spec/functional/wpns_spec.rb +41 -0
  91. data/spec/support/cert_with_password.pem +90 -0
  92. data/spec/support/cert_without_password.pem +59 -0
  93. data/spec/support/install.sh +32 -0
  94. data/spec/support/simplecov_helper.rb +20 -0
  95. data/spec/support/simplecov_quality_formatter.rb +8 -0
  96. data/spec/tmp/.gitkeep +0 -0
  97. data/spec/unit/adm/app_spec.rb +58 -0
  98. data/spec/unit/adm/notification_spec.rb +45 -0
  99. data/spec/unit/apns/app_spec.rb +29 -0
  100. data/spec/unit/apns/feedback_spec.rb +9 -0
  101. data/spec/unit/apns/notification_spec.rb +208 -0
  102. data/spec/unit/apns_feedback_spec.rb +21 -0
  103. data/spec/unit/app_spec.rb +30 -0
  104. data/spec/unit/configuration_spec.rb +45 -0
  105. data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
  106. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  107. data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
  108. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  109. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
  110. data/spec/unit/daemon/app_runner_spec.rb +292 -0
  111. data/spec/unit/daemon/batch_spec.rb +232 -0
  112. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  113. data/spec/unit/daemon/delivery_spec.rb +38 -0
  114. data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
  115. data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
  116. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
  117. data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
  118. data/spec/unit/daemon/feeder_spec.rb +98 -0
  119. data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
  120. data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
  121. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  122. data/spec/unit/daemon/retryable_error_spec.rb +14 -0
  123. data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
  124. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
  125. data/spec/unit/daemon/store/active_record_spec.rb +357 -0
  126. data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
  127. data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
  128. data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
  129. data/spec/unit/daemon_spec.rb +159 -0
  130. data/spec/unit/deprecatable_spec.rb +32 -0
  131. data/spec/unit/deprecation_spec.rb +15 -0
  132. data/spec/unit/embed_spec.rb +50 -0
  133. data/spec/unit/gcm/app_spec.rb +4 -0
  134. data/spec/unit/gcm/notification_spec.rb +36 -0
  135. data/spec/unit/logger_spec.rb +127 -0
  136. data/spec/unit/notification_shared.rb +105 -0
  137. data/spec/unit/notification_spec.rb +15 -0
  138. data/spec/unit/notifier_spec.rb +49 -0
  139. data/spec/unit/push_spec.rb +43 -0
  140. data/spec/unit/reflection_spec.rb +30 -0
  141. data/spec/unit/rpush_spec.rb +9 -0
  142. data/spec/unit/wpns/app_spec.rb +4 -0
  143. data/spec/unit/wpns/notification_spec.rb +30 -0
  144. data/spec/unit_spec_helper.rb +101 -0
  145. metadata +304 -0
@@ -0,0 +1,16 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Apns
4
+ extend ServiceConfigMethods
5
+
6
+ HOSTS = {
7
+ :production => ['gateway.push.apple.com', 2195],
8
+ :development => ['gateway.sandbox.push.apple.com', 2195], # deprecated
9
+ :sandbox => ['gateway.sandbox.push.apple.com', 2195]
10
+ }
11
+
12
+ dispatcher :tcp, :host => Proc.new { |app| HOSTS[app.environment.to_sym] }
13
+ loops Rpush::Daemon::Apns::FeedbackReceiver
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,187 @@
1
+ module Rpush
2
+ module Daemon
3
+ class AppRunner
4
+ extend Reflectable
5
+ include Reflectable
6
+ include Loggable
7
+
8
+ class << self
9
+ attr_reader :runners
10
+ end
11
+
12
+ @runners = {}
13
+
14
+ def self.enqueue(notifications)
15
+ notifications.group_by(&:app_id).each do |app_id, group|
16
+ batch = Batch.new(group)
17
+ if app = runners[app_id]
18
+ app.enqueue(batch)
19
+ else
20
+ Rpush.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.sync
26
+ apps = Rpush::App.all
27
+ apps.each { |app| sync_app(app) }
28
+ removed = runners.keys - apps.map(&:id)
29
+ removed.each { |app_id| runners.delete(app_id).stop }
30
+ end
31
+
32
+ def self.sync_app(app)
33
+ if runners[app.id]
34
+ runners[app.id].sync(app)
35
+ else
36
+ runner = new(app)
37
+ begin
38
+ runner.start
39
+ runners[app.id] = runner
40
+ rescue StandardError => e
41
+ Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
42
+ Rpush.logger.error(e)
43
+ reflect(:error, e)
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.stop
49
+ runners.values.map(&:stop)
50
+ runners.clear
51
+ end
52
+
53
+ def self.debug
54
+ runners.values.map(&:debug)
55
+ end
56
+
57
+ def self.idle
58
+ runners.values.select(&:idle?)
59
+ end
60
+
61
+ def self.wait
62
+ sleep 0.1 while !runners.values.all?(&:idle?)
63
+ end
64
+
65
+ attr_reader :app
66
+ attr_accessor :batch
67
+
68
+ def initialize(app)
69
+ @app = app
70
+ @loops = []
71
+ end
72
+
73
+ def start
74
+ app.connections.times { dispatchers.push(new_dispatcher_loop) }
75
+ start_loops
76
+ log_info("Started, #{dispatchers_str}.")
77
+ end
78
+
79
+ def stop
80
+ dispatchers.stop
81
+ stop_loops
82
+ end
83
+
84
+ def enqueue(batch)
85
+ self.batch = batch
86
+ batch.notifications.each do |notification|
87
+ queue.push([notification, batch])
88
+ reflect(:notification_enqueued, notification)
89
+ end
90
+ end
91
+
92
+ def sync(app)
93
+ @app = app
94
+ diff = dispatchers.size - app.connections
95
+ return if diff == 0
96
+ if diff > 0
97
+ decrement_dispatchers(diff)
98
+ log_info("Stopped #{dispatchers_str(diff)}. #{dispatchers_str} running.")
99
+ else
100
+ increment_dispatchers(diff.abs)
101
+ log_info("Started #{dispatchers_str(diff)}. #{dispatchers_str} running.")
102
+ end
103
+ end
104
+
105
+ def decrement_dispatchers(num)
106
+ num.times { dispatchers.pop }
107
+ end
108
+
109
+ def increment_dispatchers(num)
110
+ num.times { dispatchers.push(new_dispatcher_loop) }
111
+ end
112
+
113
+ def debug
114
+ Rpush.logger.info <<-EOS
115
+
116
+ #{@app.name}:
117
+ dispatchers: #{num_dispatchers}
118
+ queued: #{queue_size}
119
+ batch size: #{batch_size}
120
+ batch processed: #{batch_processed}
121
+ idle: #{idle?}
122
+ EOS
123
+ end
124
+
125
+ def idle?
126
+ batch ? batch.complete? : true
127
+ end
128
+
129
+ def queue_size
130
+ queue.size
131
+ end
132
+
133
+ def batch_size
134
+ batch ? batch.num_notifications : 0
135
+ end
136
+
137
+ def batch_processed
138
+ batch ? batch.num_processed : 0
139
+ end
140
+
141
+ def num_dispatchers
142
+ dispatchers.size
143
+ end
144
+
145
+ protected
146
+
147
+ def start_loops
148
+ service_module.loops.each do |loop_class|
149
+ instance = loop_class.new(@app)
150
+ instance.start
151
+ @loops << instance
152
+ end
153
+ end
154
+
155
+ def stop_loops
156
+ @loops.map(&:stop)
157
+ @loops = []
158
+ end
159
+
160
+ def new_dispatcher_loop
161
+ dispatcher = service_module.new_dispatcher(@app)
162
+ dispatcher_loop = Rpush::Daemon::DispatcherLoop.new(queue, dispatcher)
163
+ dispatcher_loop.start
164
+ dispatcher_loop
165
+ end
166
+
167
+ def service_module
168
+ return @service_module if defined? @service_module
169
+ @service_module = "Rpush::Daemon::#{@app.service_name.camelize}".constantize
170
+ end
171
+
172
+ def queue
173
+ @queue ||= Queue.new
174
+ end
175
+
176
+ def dispatchers
177
+ @dispatchers ||= Rpush::Daemon::DispatcherLoopCollection.new
178
+ end
179
+
180
+ def dispatchers_str(count = app.connections)
181
+ count = count.abs
182
+ str = count == 1 ? 'dispatcher' : 'dispatchers'
183
+ "#{count} #{str}"
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,115 @@
1
+ module Rpush
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 Rpush.config.batch_storage_updates
24
+ retryable[deliver_after] ||= []
25
+ retryable[deliver_after] << notification
26
+ Rpush::Daemon.store.mark_retryable(notification, deliver_after, :persist => false)
27
+ else
28
+ Rpush::Daemon.store.mark_retryable(notification, deliver_after)
29
+ reflect(:notification_will_retry, notification)
30
+ end
31
+ end
32
+
33
+ def mark_delivered(notification)
34
+ if Rpush.config.batch_storage_updates
35
+ delivered << notification
36
+ Rpush::Daemon.store.mark_delivered(notification, Time.now, :persist => false)
37
+ else
38
+ Rpush::Daemon.store.mark_delivered(notification, Time.now)
39
+ reflect(:notification_delivered, notification)
40
+ end
41
+ end
42
+
43
+ def mark_failed(notification, code, description)
44
+ if Rpush.config.batch_storage_updates
45
+ key = [code, description]
46
+ failed[key] ||= []
47
+ failed[key] << notification
48
+ Rpush::Daemon.store.mark_failed(notification, code, description, Time.now, :persist => false)
49
+ else
50
+ Rpush::Daemon.store.mark_failed(notification, code, description, Time.now)
51
+ reflect(:notification_failed, notification)
52
+ end
53
+ end
54
+
55
+ def notification_dispatched
56
+ @mutex.synchronize do
57
+ @num_processed += 1
58
+ complete if @num_processed >= @notifications.size
59
+ end
60
+ end
61
+
62
+ def complete?
63
+ @complete == true
64
+ end
65
+
66
+ def describe
67
+ notifications.map(&:id).join(', ')
68
+ end
69
+
70
+ private
71
+
72
+ def complete
73
+ [:complete_delivered, :complete_failed, :complete_retried].each do |method|
74
+ begin
75
+ send(method)
76
+ rescue StandardError => e
77
+ Rpush.logger.error(e)
78
+ reflect(:error, e)
79
+ end
80
+ end
81
+
82
+ notifications.clear
83
+ @complete = true
84
+ end
85
+
86
+ def complete_delivered
87
+ Rpush::Daemon.store.mark_batch_delivered(delivered)
88
+ delivered.each do |notification|
89
+ reflect(:notification_delivered, notification)
90
+ end
91
+ delivered.clear
92
+ end
93
+
94
+ def complete_failed
95
+ failed.each do |(code, description), notifications|
96
+ Rpush::Daemon.store.mark_batch_failed(notifications, code, description)
97
+ notifications.each do |notification|
98
+ reflect(:notification_failed, notification)
99
+ end
100
+ end
101
+ failed.clear
102
+ end
103
+
104
+ def complete_retried
105
+ retryable.each do |deliver_after, notifications|
106
+ Rpush::Daemon.store.mark_batch_retryable(notifications, deliver_after)
107
+ notifications.each do |notification|
108
+ reflect(:notification_will_retry, notification)
109
+ end
110
+ end
111
+ retryable.clear
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,59 @@
1
+ module Rpush
2
+ module Daemon
3
+ HTTP_STATUS_CODES = {
4
+ 100 => 'Continue',
5
+ 101 => 'Switching Protocols',
6
+ 102 => 'Processing',
7
+ 200 => 'OK',
8
+ 201 => 'Created',
9
+ 202 => 'Accepted',
10
+ 203 => 'Non-Authoritative Information',
11
+ 204 => 'No Content',
12
+ 205 => 'Reset Content',
13
+ 206 => 'Partial Content',
14
+ 207 => 'Multi-Status',
15
+ 226 => 'IM Used',
16
+ 300 => 'Multiple Choices',
17
+ 301 => 'Moved Permanently',
18
+ 302 => 'Found',
19
+ 303 => 'See Other',
20
+ 304 => 'Not Modified',
21
+ 305 => 'Use Proxy',
22
+ 306 => 'Reserved',
23
+ 307 => 'Temporary Redirect',
24
+ 400 => 'Bad Request',
25
+ 401 => 'Unauthorized',
26
+ 402 => 'Payment Required',
27
+ 403 => 'Forbidden',
28
+ 404 => 'Not Found',
29
+ 405 => 'Method Not Allowed',
30
+ 406 => 'Not Acceptable',
31
+ 407 => 'Proxy Authentication Required',
32
+ 408 => 'Request Timeout',
33
+ 409 => 'Conflict',
34
+ 410 => 'Gone',
35
+ 411 => 'Length Required',
36
+ 412 => 'Precondition Failed',
37
+ 413 => 'Request Entity Too Large',
38
+ 414 => 'Request-URI Too Long',
39
+ 415 => 'Unsupported Media Type',
40
+ 416 => 'Requested Range Not Satisfiable',
41
+ 417 => 'Expectation Failed',
42
+ 418 => "I'm a Teapot",
43
+ 422 => 'Unprocessable Entity',
44
+ 423 => 'Locked',
45
+ 424 => 'Failed Dependency',
46
+ 426 => 'Upgrade Required',
47
+ 429 => 'Too Many Requests',
48
+ 500 => 'Internal Server Error',
49
+ 501 => 'Not Implemented',
50
+ 502 => 'Bad Gateway',
51
+ 503 => 'Service Unavailable',
52
+ 504 => 'Gateway Timeout',
53
+ 505 => 'HTTP Version Not Supported',
54
+ 506 => 'Variant Also Negotiates',
55
+ 507 => 'Insufficient Storage',
56
+ 510 => 'Not Extended'
57
+ }
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ module Rpush
2
+ module Daemon
3
+ class Delivery
4
+ include Reflectable
5
+ include Loggable
6
+
7
+ def mark_retryable(notification, deliver_after)
8
+ if notification.fail_after && notification.fail_after < Time.now
9
+ @batch.mark_failed(notification, nil, "Notification failed to be delivered before #{notification.fail_after.strftime("%Y-%m-%d %H:%M:%S")}.")
10
+ else
11
+ @batch.mark_retryable(notification, deliver_after)
12
+ end
13
+ end
14
+
15
+ def mark_retryable_exponential(notification)
16
+ mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
17
+ end
18
+
19
+ def mark_delivered
20
+ @batch.mark_delivered(@notification)
21
+ end
22
+
23
+ def mark_failed(code, description)
24
+ @batch.mark_failed(@notification, code, description)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module Rpush
2
+ class DeliveryError < StandardError
3
+ attr_reader :code, :description
4
+
5
+ def initialize(code, notification_id, description)
6
+ @code = code
7
+ @notification_id = notification_id
8
+ @description = description
9
+ end
10
+
11
+ def to_s
12
+ message
13
+ end
14
+
15
+ def message
16
+ "Unable to deliver notification #{@notification_id}, received error #{@code} (#{@description})"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Dispatcher
4
+ class Http
5
+ def initialize(app, delivery_class, options = {})
6
+ @app = app
7
+ @delivery_class = delivery_class
8
+ @http = Net::HTTP::Persistent.new('rpush')
9
+ end
10
+
11
+ def dispatch(notification, batch)
12
+ @delivery_class.new(@app, @http, notification, batch).perform
13
+ end
14
+
15
+ def cleanup
16
+ @http.shutdown
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Dispatcher
4
+ class Tcp
5
+ def initialize(app, delivery_class, options = {})
6
+ @app = app
7
+ @delivery_class = delivery_class
8
+ @host, @port = options[:host].call(@app)
9
+ end
10
+
11
+ def dispatch(notification, batch)
12
+ @delivery_class.new(@app, connection, notification, batch).perform
13
+ end
14
+
15
+ def cleanup
16
+ @connection.close if @connection
17
+ end
18
+
19
+ private
20
+
21
+ def connection
22
+ return @connection if defined? @connection
23
+ connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
24
+ connection.connect
25
+ @connection = connection
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,54 @@
1
+ module Rpush
2
+ module Daemon
3
+ class DispatcherLoop
4
+ include Reflectable
5
+
6
+ WAKEUP = :wakeup
7
+
8
+ def initialize(queue, dispatcher)
9
+ @queue = queue
10
+ @dispatcher = dispatcher
11
+ end
12
+
13
+ def start
14
+ @thread = Thread.new do
15
+ loop do
16
+ dispatch
17
+ break if @stop
18
+ end
19
+
20
+ Rpush::Daemon.store.release_connection
21
+ end
22
+ end
23
+
24
+ def stop
25
+ @stop = true
26
+ end
27
+
28
+ def wakeup
29
+ @queue.push(WAKEUP) if @thread
30
+ end
31
+
32
+ def wait
33
+ @thread.join if @thread
34
+ @dispatcher.cleanup
35
+ end
36
+
37
+ protected
38
+
39
+ def dispatch
40
+ notification, batch = @queue.pop
41
+ return if notification == WAKEUP
42
+
43
+ begin
44
+ @dispatcher.dispatch(notification, batch)
45
+ rescue StandardError => e
46
+ Rpush.logger.error(e)
47
+ reflect(:error, e)
48
+ ensure
49
+ batch.notification_dispatched
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,33 @@
1
+ module Rpush
2
+ module Daemon
3
+ class DispatcherLoopCollection
4
+ attr_reader :loops
5
+
6
+ def initialize
7
+ @loops = []
8
+ end
9
+
10
+ def push(dispatcher_loop)
11
+ @loops << dispatcher_loop
12
+ end
13
+
14
+ def pop
15
+ dispatcher_loop = @loops.pop
16
+ dispatcher_loop.stop
17
+ dispatcher_loop.wakeup
18
+ @loops.map(&:wakeup)
19
+ dispatcher_loop.wait
20
+ end
21
+
22
+ def size
23
+ @loops.size
24
+ end
25
+
26
+ def stop
27
+ @loops.map(&:stop)
28
+ @loops.map(&:wakeup)
29
+ @loops.map(&:wait)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,68 @@
1
+ module Rpush
2
+ module Daemon
3
+ class Feeder
4
+ extend Reflectable
5
+
6
+ def self.start
7
+ @stop = false
8
+
9
+ if Rpush.config.embedded
10
+ Thread.new { feed_forever }
11
+ elsif Rpush.config.push
12
+ enqueue_notifications
13
+ else
14
+ feed_forever
15
+ end
16
+ end
17
+
18
+ def self.stop
19
+ @stop = true
20
+ interrupt_sleep
21
+ end
22
+
23
+ def self.interrupt_sleep
24
+ interruptible_sleeper.interrupt_sleep
25
+ end
26
+
27
+ protected
28
+
29
+ def self.feed_forever
30
+ loop do
31
+ enqueue_notifications
32
+ interruptible_sleeper.sleep(Rpush.config.push_poll)
33
+ break if stop?
34
+ end
35
+
36
+ Rpush::Daemon.store.release_connection
37
+ end
38
+
39
+ # :nocov:
40
+ def self.stop?
41
+ @stop
42
+ end
43
+
44
+ def self.enqueue_notifications
45
+ begin
46
+ idle = Rpush::Daemon::AppRunner.idle.map(&:app)
47
+ return if idle.empty?
48
+ notifications = Rpush::Daemon.store.deliverable_notifications(idle)
49
+ Rpush::Daemon::AppRunner.enqueue(notifications)
50
+ rescue StandardError => e
51
+ Rpush.logger.error(e)
52
+ reflect(:error, e)
53
+ end
54
+ end
55
+
56
+ def self.interruptible_sleeper
57
+ return @interruptible_sleeper if @interruptible_sleeper
58
+
59
+ @interruptible_sleeper = InterruptibleSleep.new
60
+ if Rpush.config.wakeup
61
+ @interruptible_sleeper.enable_wake_on_udp Rpush.config.wakeup[:bind], Rpush.config.wakeup[:port]
62
+ end
63
+
64
+ @interruptible_sleeper
65
+ end
66
+ end
67
+ end
68
+ end