rapns 3.0.1-java → 3.1.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 (46) hide show
  1. data/CHANGELOG.md +10 -2
  2. data/README.md +26 -3
  3. data/bin/rapns +2 -7
  4. data/lib/generators/templates/add_gcm.rb +3 -1
  5. data/lib/generators/templates/rapns.rb +42 -11
  6. data/lib/rapns.rb +11 -1
  7. data/lib/rapns/apns/notification.rb +8 -2
  8. data/lib/rapns/app.rb +3 -3
  9. data/lib/rapns/configuration.rb +46 -13
  10. data/lib/rapns/daemon.rb +33 -22
  11. data/lib/rapns/daemon/apns/connection.rb +12 -9
  12. data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
  13. data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
  14. data/lib/rapns/daemon/app_runner.rb +23 -7
  15. data/lib/rapns/daemon/delivery.rb +5 -1
  16. data/lib/rapns/daemon/delivery_handler.rb +4 -0
  17. data/lib/rapns/daemon/feeder.rb +26 -5
  18. data/lib/rapns/daemon/reflectable.rb +13 -0
  19. data/lib/rapns/embed.rb +28 -0
  20. data/lib/rapns/gcm/notification.rb +7 -2
  21. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  22. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  23. data/lib/rapns/push.rb +12 -0
  24. data/lib/rapns/reflection.rb +44 -0
  25. data/lib/rapns/version.rb +1 -1
  26. data/spec/support/cert_with_password.pem +90 -0
  27. data/spec/support/cert_without_password.pem +59 -0
  28. data/spec/unit/apns/app_spec.rb +15 -1
  29. data/spec/unit/apns/notification_spec.rb +16 -1
  30. data/spec/unit/configuration_spec.rb +10 -1
  31. data/spec/unit/daemon/apns/connection_spec.rb +11 -2
  32. data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
  33. data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
  34. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
  35. data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
  36. data/spec/unit/daemon/feeder_spec.rb +37 -6
  37. data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
  38. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  39. data/spec/unit/daemon_spec.rb +55 -9
  40. data/spec/unit/embed_spec.rb +44 -0
  41. data/spec/unit/gcm/notification_spec.rb +9 -3
  42. data/spec/unit/push_spec.rb +28 -0
  43. data/spec/unit/reflection_spec.rb +34 -0
  44. data/spec/unit_spec_helper.rb +4 -62
  45. metadata +22 -5
  46. data/lib/rapns/gcm/payload_size_validator.rb +0 -13
data/CHANGELOG.md CHANGED
@@ -1,7 +1,15 @@
1
- ## 3.0.1 (Sun 16, 2012)
1
+ ## 3.1.0 (Jan 26, 2013)
2
+ * Rapns.reflect API for fine-grained introspection.
3
+ * Rapns.embed API for embedding Rapns into an existing process.
4
+ * Rapns.push API for using Rapns in scheduled jobs.
5
+ * Fix issue with integration with ActiveScaffold (#98) (@jeffarena).
6
+ * Fix content-available setter for APNs (#95) (@dup2).
7
+ * GCM validation fixes (#96) (@DianthuDia).
8
+
9
+ ## 3.0.1 (Dec 16, 2012)
2
10
  * Fix compatibility with Rails 3.0.x. Fixes #89.
3
11
 
4
- ## 3.0.0 (Sat 15, 2012)
12
+ ## 3.0.0 (Dec 15, 2012)
5
13
  * Add support for Google Cloud Messaging.
6
14
  * Fix Heroku logging issue.
7
15
 
data/README.md CHANGED
@@ -1,14 +1,18 @@
1
1
  [![Build Status](https://secure.travis-ci.org/ileitch/rapns.png?branch=master)](http://travis-ci.org/ileitch/rapns)
2
2
 
3
- ### Rapns - Professional grade APNs and GCM daemon
3
+ ### Rapns - Professional grade APNs and GCM for Ruby.
4
4
 
5
5
  * Supports both APNs (iOS) and GCM (Google Cloud Messaging, Android).
6
6
  * Seamless Rails integration.
7
7
  * Scalable - choose the number of threads each app spawns.
8
8
  * Designed for uptime - signal -HUP to add, update apps.
9
9
  * Stable - reconnects database and network connections when lost.
10
+ * Run as a daemon or inside an existing process.
11
+ * Use in a scheduler for low-workload deployments ([Push API](rapns/wiki/Push-API)).
12
+ * Reflection API for fine-grained instrumentation ([Reflection API](rapns/wiki/Relfection-API)).
10
13
  * Works with MRI, JRuby, Rubinius 1.8 and 1.9.
11
14
  * [Airbrake](http://airbrakeapp.com/) integration.
15
+ * Built with a love for Open Source :)
12
16
 
13
17
  #### 2.x users please read [upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
14
18
 
@@ -36,11 +40,16 @@ Generate the migrations, rapns.yml and migrate:
36
40
  3. Select both the certificate and private key.
37
41
  4. Right click and select `Export 2 items...`.
38
42
  5. Save the file as `cert.p12`, make sure the File Format is `Personal Information Exchange (p12)`.
39
- 6. If you decide to set a password for your exported certificate, please read the 'Adding Apps' section below.
40
- 7. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
43
+ 6. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
44
+
45
+ Without a password:
41
46
 
42
47
  `openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
43
48
 
49
+ With a password:
50
+
51
+ `openssl pkcs12 -clcerts -in cert.p12 -out <environment>.pem`
52
+
44
53
  ## Create an App
45
54
 
46
55
  #### APNs
@@ -86,9 +95,17 @@ n.save!
86
95
 
87
96
  ## Starting Rapns
88
97
 
98
+ As a daemon:
99
+
89
100
  cd /path/to/rails/app
90
101
  rapns <Rails environment> [options]
91
102
 
103
+ Inside an existing process:
104
+
105
+ Rapns.embed
106
+
107
+ *Please note that only ever a single instance of Rapns should be running.*
108
+
92
109
  See [Configuration](rapns/wiki/Configuration) for a list of options, or run `rapns --help`.
93
110
 
94
111
  ## Updating Rapns
@@ -102,6 +119,9 @@ After updating you should run `rails g rapns` to check for any new migrations.
102
119
  * [Upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
103
120
  * [Deploying to Heroku](rapns/wiki/Heroku)
104
121
  * [Hot App Updates](rapns/wiki/Hot-App-Updates)
122
+ * [Reflection API](rapns/wiki/Reflection-API)
123
+ * [Push API](rapns/wiki/Push-API)
124
+ * [Embedding API](rapns/wiki/Embedding-API)
105
125
 
106
126
  ### APNs
107
127
  * [Advanced APNs Features](rapns/wiki/Advanced-APNs-Features)
@@ -138,3 +158,6 @@ Thank you to the following wonderful people for contributing:
138
158
  * [@adorr](https://github.com/adorr)
139
159
  * [@mattconnolly](https://github.com/mattconnolly)
140
160
  * [@emeitch](https://github.com/emeitch)
161
+ * [@jeffarena](https://github.com/jeffarena)
162
+ * [@DianthuDia](https://github.com/DianthuDia)
163
+ * [@dup2](https://github.com/dup2)
data/bin/rapns CHANGED
@@ -11,9 +11,7 @@ if environment.nil? || environment =~ /^-/
11
11
  exit 1
12
12
  end
13
13
 
14
- # A mock configuration to be used before the Rails environment is loaded.
15
- class CommandlineConfig < Struct.new(*Rapns::CONFIG_ATTRS); end
16
- config = CommandlineConfig.new
14
+ config = Rapns::ConfigurationWithoutDefaults.new
17
15
 
18
16
  ARGV.options do |opts|
19
17
  opts.banner = banner
@@ -34,8 +32,5 @@ load 'config/environment.rb'
34
32
  load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
35
33
 
36
34
  Rapns.config.update(config)
37
-
38
- require 'rapns/daemon'
39
- require 'rapns/patches'
40
-
35
+ Rapns.require_for_daemon
41
36
  Rapns::Daemon.start
@@ -34,7 +34,9 @@ class AddGcm < ActiveRecord::Migration
34
34
 
35
35
  add_column :rapns_notifications, :collapse_key, :string, :null => true
36
36
  add_column :rapns_notifications, :delay_while_idle, :boolean, :null => false, :default => false
37
- add_column :rapns_notifications, :registration_ids, :text, :null => true
37
+
38
+ reg_ids_type = ActiveRecord::Base.connection.adapter_name.include?('Mysql') ? :mediumtext : :text
39
+ add_column :rapns_notifications, :registration_ids, reg_ids_type, :null => true
38
40
  add_column :rapns_notifications, :app_id, :integer, :null => true
39
41
  add_column :rapns_notifications, :retries, :integer, :null => true, :default => 0
40
42
 
@@ -23,17 +23,48 @@
23
23
  # Path to write PID file. Relative to Rails root unless absolute.
24
24
  # config.pid_file = '/path/to/rapns.pid'
25
25
 
26
- # Define a block that will be called with a Rapns::Apns::Feedback instance
27
- # when feedback is received from the APNs that a notification has
28
- # failed to be delivered. Further notifications should not be sent to the device.
26
+ end
27
+
28
+ Rapns.reflect do |on|
29
+
30
+ # Called with a Rapns::Apns::Feedback instance when feedback is received
31
+ # from the APNs that a notification has failed to be delivered.
32
+ # Further notifications should not be sent to the device.
33
+ # on.apns_feedback do |feedback|
34
+ # end
35
+
36
+ # Called when a notification is queued internally for delivery.
37
+ # The internal queue for each app runner can be inspected:
29
38
  #
30
- # Example:
31
- # config.on_apns_feedback do |feedback|
32
- # device = Device.find_by_device_token(feedback.device_token)
33
- # if device
34
- # device.active = false
35
- # device.save!
36
- # end
39
+ # Rapns::Daemon::AppRunner.runners.each do |app_id, runner|
40
+ # runner.app
41
+ # runner.queue_size
42
+ # end
43
+ #
44
+ # on.notification_enqueued do |notification|
37
45
  # end
38
46
 
39
- end
47
+ # Called when a notification is successfully delivered.
48
+ # on.notification_delivered do |notification|
49
+ # end
50
+
51
+ # Called when notification delivery failed.
52
+ # Call 'error_code' and 'error_description' on the notification for the cause.
53
+ # on.notification_failed do |notification|
54
+ # end
55
+
56
+ # Called when a notification will be retried at a later date.
57
+ # Call 'deliver_after' on the notification for the next delivery date
58
+ # and 'retries' for the number of times this notification has been retried.
59
+ # on.notification_will_retry do |notification|
60
+ # end
61
+
62
+ # Called when an APNs connection is lost and will be reconnected.
63
+ # on.apns_connection_lost do |app, error|
64
+ # end
65
+
66
+ # Called when an exception is raised.
67
+ # on.error do |error|
68
+ # end
69
+
70
+ end
data/lib/rapns.rb CHANGED
@@ -8,6 +8,9 @@ require 'rapns/multi_json_helper'
8
8
  require 'rapns/notification'
9
9
  require 'rapns/app'
10
10
  require 'rapns/configuration'
11
+ require 'rapns/reflection'
12
+ require 'rapns/embed'
13
+ require 'rapns/push'
11
14
 
12
15
  require 'rapns/apns/binary_notification_validator'
13
16
  require 'rapns/apns/device_token_format_validator'
@@ -17,7 +20,14 @@ require 'rapns/apns/feedback'
17
20
  require 'rapns/apns/app'
18
21
 
19
22
  require 'rapns/gcm/expiry_collapse_key_mutual_inclusion_validator'
20
- require 'rapns/gcm/payload_size_validator'
23
+ require 'rapns/gcm/payload_data_size_validator'
24
+ require 'rapns/gcm/registration_ids_count_validator'
21
25
  require 'rapns/gcm/notification'
22
26
  require 'rapns/gcm/app'
23
27
 
28
+ module Rapns
29
+ def self.require_for_daemon
30
+ require 'rapns/daemon'
31
+ require 'rapns/patches'
32
+ end
33
+ end
@@ -43,13 +43,13 @@ module Rapns
43
43
 
44
44
  MDM_KEY = '__rapns_mdm__'
45
45
  def mdm=(magic)
46
- self.attributes_for_device = { MDM_KEY => magic }
46
+ self.attributes_for_device = (attributes_for_device || {}).merge({ MDM_KEY => magic })
47
47
  end
48
48
 
49
49
  CONTENT_AVAILABLE_KEY = '__rapns_content_available__'
50
50
  def content_available=(bool)
51
51
  return unless bool
52
- self.attributes_for_device = { CONTENT_AVAILABLE_KEY => true }
52
+ self.attributes_for_device = (attributes_for_device || {}).merge({ CONTENT_AVAILABLE_KEY => true })
53
53
  end
54
54
 
55
55
  def as_json
@@ -81,6 +81,12 @@ module Rapns
81
81
  [1, id_for_pack, expiry, 0, 32, device_token, payload_size, payload].pack("cNNccH*na*")
82
82
  end
83
83
 
84
+ def data=(attrs)
85
+ return unless attrs
86
+ raise ArgumentError, "must be a Hash" if !attrs.is_a?(Hash)
87
+ super attrs.merge(data || {})
88
+ end
89
+
84
90
  end
85
91
  end
86
92
  end
data/lib/rapns/app.rb CHANGED
@@ -4,7 +4,7 @@ module Rapns
4
4
 
5
5
  attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key
6
6
 
7
- has_many :notifications
7
+ has_many :notifications, :class_name => 'Rapns::Notification'
8
8
 
9
9
  validates :name, :presence => true, :uniqueness => { :scope => [:type, :environment] }
10
10
  validates_numericality_of :connections, :greater_than => 0, :only_integer => true
@@ -16,8 +16,8 @@ module Rapns
16
16
  def certificate_has_matching_private_key
17
17
  result = false
18
18
  if certificate.present?
19
- x509 = OpenSSL::X509::Certificate.new certificate rescue nil
20
- pkey = OpenSSL::PKey::RSA.new certificate rescue nil
19
+ x509 = OpenSSL::X509::Certificate.new(certificate) rescue nil
20
+ pkey = OpenSSL::PKey::RSA.new(certificate, password) rescue nil
21
21
  result = !x509.nil? && !pkey.nil?
22
22
  unless result
23
23
  errors.add :certificate, 'Certificate value must contain a certificate and a private key.'
@@ -7,21 +7,21 @@ module Rapns
7
7
  yield config if block_given?
8
8
  end
9
9
 
10
- CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll,
11
- :airbrake_notify, :check_for_errors, :pid_file, :batch_size]
10
+ CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
11
+ :airbrake_notify, :check_for_errors, :pid_file, :batch_size,
12
+ :push]
13
+
14
+ class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
15
+ end
12
16
 
13
17
  class Configuration < Struct.new(*CONFIG_ATTRS)
18
+ include Deprecatable
19
+
14
20
  attr_accessor :apns_feedback_callback
15
21
 
16
22
  def initialize
17
23
  super
18
-
19
- self.foreground = false
20
- self.push_poll = 2
21
- self.feedback_poll = 60
22
- self.airbrake_notify = true
23
- self.check_for_errors = true
24
- self.batch_size = 5000
24
+ set_defaults
25
25
  end
26
26
 
27
27
  def update(other)
@@ -31,10 +31,6 @@ module Rapns
31
31
  end
32
32
  end
33
33
 
34
- def on_apns_feedback(&block)
35
- self.apns_feedback_callback = block
36
- end
37
-
38
34
  def pid_file=(path)
39
35
  if path && !Pathname.new(path).absolute?
40
36
  super(File.join(Rails.root, path))
@@ -42,5 +38,42 @@ module Rapns
42
38
  super
43
39
  end
44
40
  end
41
+
42
+ def foreground=(bool)
43
+ if defined? JRUBY_VERSION
44
+ # The JVM does not support fork().
45
+ super(true)
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def on_apns_feedback(&block)
52
+ self.apns_feedback_callback = block
53
+ end
54
+ deprecated(:on_apns_feedback, 3.2, "Please use the Rapns.reflect API instead.")
55
+
56
+ private
57
+
58
+ def set_defaults
59
+ if defined? JRUBY_VERSION
60
+ # The JVM does not support fork().
61
+ self.foreground = true
62
+ else
63
+ self.foreground = false
64
+ end
65
+
66
+ self.push_poll = 2
67
+ self.feedback_poll = 60
68
+ self.airbrake_notify = true
69
+ self.check_for_errors = true
70
+ self.batch_size = 5000
71
+ self.pid_file = nil
72
+ self.apns_feedback_callback = nil
73
+
74
+ # Internal options.
75
+ self.embedded = false
76
+ self.push = false
77
+ end
45
78
  end
46
79
  end
data/lib/rapns/daemon.rb CHANGED
@@ -5,6 +5,7 @@ require 'openssl'
5
5
 
6
6
  require 'net/http/persistent'
7
7
 
8
+ require 'rapns/daemon/reflectable'
8
9
  require 'rapns/daemon/interruptible_sleep'
9
10
  require 'rapns/daemon/delivery_error'
10
11
  require 'rapns/daemon/database_reconnectable'
@@ -37,9 +38,10 @@ module Rapns
37
38
  def self.start
38
39
  self.logger = Logger.new(:foreground => Rapns.config.foreground,
39
40
  :airbrake_notify => Rapns.config.airbrake_notify)
40
- setup_signal_hooks
41
41
 
42
- unless Rapns.config.foreground
42
+ setup_signal_traps if trap_signals?
43
+
44
+ if daemonize?
43
45
  daemonize
44
46
  reconnect_database
45
47
  end
@@ -47,11 +49,22 @@ module Rapns
47
49
  write_pid_file
48
50
  ensure_upgraded
49
51
  AppRunner.sync
50
- Feeder.start(Rapns.config.push_poll)
52
+ Feeder.start
53
+ end
54
+
55
+ def self.shutdown(quiet = false)
56
+ puts "\nShutting down..." unless quiet
57
+ Feeder.stop
58
+ AppRunner.stop
59
+ delete_pid_file
51
60
  end
52
61
 
53
62
  protected
54
63
 
64
+ def self.daemonize?
65
+ !(Rapns.config.foreground || Rapns.config.embedded || Rapns.config.push || defined?(JRUBY_VERSION))
66
+ end
67
+
55
68
  def self.ensure_upgraded
56
69
  count = 0
57
70
 
@@ -64,7 +77,7 @@ module Rapns
64
77
  puts "Please run 'rails g rapns' to generate the new migrations and create your app."
65
78
  puts "See https://github.com/ileitch/rapns for further instructions."
66
79
  puts
67
- exit 1
80
+ exit 1 unless Rapns.config.embedded || Rapns.config.push
68
81
  end
69
82
 
70
83
  if count == 0
@@ -80,7 +93,11 @@ Remove config/rapns/rapns.yml to avoid this warning.
80
93
  end
81
94
  end
82
95
 
83
- def self.setup_signal_hooks
96
+ def self.trap_signals?
97
+ !(Rapns.config.embedded || Rapns.config.push)
98
+ end
99
+
100
+ def self.setup_signal_traps
84
101
  @shutting_down = false
85
102
 
86
103
  Signal.trap('SIGHUP') { AppRunner.sync }
@@ -97,13 +114,6 @@ Remove config/rapns/rapns.yml to avoid this warning.
97
114
  shutdown
98
115
  end
99
116
 
100
- def self.shutdown
101
- puts "\nShutting down..."
102
- Feeder.stop
103
- AppRunner.stop
104
- delete_pid_file
105
- end
106
-
107
117
  def self.write_pid_file
108
118
  if !Rapns.config.pid_file.blank?
109
119
  begin
@@ -121,16 +131,17 @@ Remove config/rapns/rapns.yml to avoid this warning.
121
131
 
122
132
  # :nocov:
123
133
  def self.daemonize
124
- exit if pid = fork
125
- Process.setsid
126
- exit if pid = fork
127
-
128
- Dir.chdir '/'
129
- File.umask 0000
130
-
131
- STDIN.reopen '/dev/null'
132
- STDOUT.reopen '/dev/null', 'a'
133
- STDERR.reopen STDOUT
134
+ if RUBY_VERSION < "1.9"
135
+ exit if fork
136
+ Process.setsid
137
+ exit if fork
138
+ Dir.chdir "/"
139
+ STDIN.reopen "/dev/null"
140
+ STDOUT.reopen "/dev/null", "a"
141
+ STDERR.reopen "/dev/null", "a"
142
+ else
143
+ Process.daemon
144
+ end
134
145
  end
135
146
  end
136
147
  end