rapns 3.0.1-java → 3.1.0-java

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