This class is capable of spawning instances of a single Ruby on Rails application. It does so by preloading as much of the application‘s code as possible, then creating instances of the application using what is already preloaded. This makes it spawning application instances very fast, except for the first spawn.

Use multiple instances of ApplicationSpawner if you need to spawn multiple different Ruby on Rails applications.

Note: ApplicationSpawner may only be started asynchronously with AbstractServer#start. Starting it synchronously with AbstractServer#start_synchronously has not been tested.

Methods
Included Modules
Classes and Modules
Class PhusionPassenger::Railz::ApplicationSpawner::Error
Constants
ROOT_UID = 0
  The user ID of the root user.
ROOT_GID = 0
  The group ID of the root user.
Attributes
[R] app_root The application root of this spawner.
Public Class methods
new(app_root, options = {})

app_root is the root directory of this application, i.e. the directory that contains ‘app/’, ‘public/’, etc. If given an invalid directory, or a directory that doesn‘t appear to be a Rails application root directory, then an InvalidPath will be raised.

Additional options are:

  • lower_privilege and lowest_user: If lower_privilege is true, then ApplicationSpawner will attempt to switch to the user who owns the application‘s config/environment.rb, and to the default group of that user.

    If that user doesn‘t exist on the system, or if that user is root, then ApplicationSpawner will attempt to switch to the username given by lowest_user (and to the default group of that user). If lowest_user doesn‘t exist either, or if switching user failed (because the current process does not have the privilege to do so), then ApplicationSpawner will continue without reporting an error.

  • environment: Allows one to specify the RAILS_ENV environment to use.

All other options will be passed on to RequestHandler.

    # File lib/phusion_passenger/railz/application_spawner.rb, line 83
83:         def initialize(app_root, options = {})
84:                 super()
85:                 begin
86:                         @app_root = normalize_path(app_root)
87:                 rescue SystemCallError => e
88:                         raise InvalidPath, e.message
89:                 rescue InvalidPath
90:                         raise
91:                 end
92:                 @options = sanitize_spawn_options(options)
93:                 @lower_privilege = @options["lower_privilege"]
94:                 @lowest_user     = @options["lowest_user"]
95:                 @environment     = @options["environment"]
96:                 self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME
97:                 assert_valid_app_root(@app_root)
98:                 define_message_handler(:spawn_application, :handle_spawn_application)
99:         end
Public Instance methods
spawn_application()

Spawn an instance of the RoR application. When successful, an Application object will be returned, which represents the spawned RoR application.

Raises:

     # File lib/phusion_passenger/railz/application_spawner.rb, line 107
107:         def spawn_application
108:                 server.write("spawn_application")
109:                 pid, socket_name, socket_type = server.read
110:                 if pid.nil?
111:                         raise IOError, "Connection closed"
112:                 end
113:                 owner_pipe = server.recv_io
114:                 return Application.new(@app_root, pid, socket_name,
115:                         socket_type, owner_pipe)
116:         rescue SystemCallError, IOError, SocketError => e
117:                 raise Error, "The application spawner server exited unexpectedly: #{e}"
118:         end
spawn_application!()

Spawn an instance of the RoR application. When successful, an Application object will be returned, which represents the spawned RoR application.

Unlike spawn_application, this method may be called even when the ApplicationSpawner server isn‘t started. This allows one to spawn a RoR application without preloading any source files.

This method may only be called if no Rails framework has been loaded in the current Ruby VM.

Raises:

  • AppInitError: The Ruby on Rails application raised an exception or called exit() during startup.
  • SystemCallError, IOError, SocketError: Something went wrong.
     # File lib/phusion_passenger/railz/application_spawner.rb, line 134
134:         def spawn_application!
135:                 a, b = UNIXSocket.pair
136:                 pid = safe_fork('application', true) do
137:                         begin
138:                                 a.close
139:                                 
140:                                 file_descriptors_to_leave_open = [0, 1, 2, b.fileno]
141:                                 NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open)
142:                                 close_all_io_objects_for_fds(file_descriptors_to_leave_open)
143:                                 
144:                                 channel = MessageChannel.new(b)
145:                                 success = report_app_init_status(channel) do
146:                                         ENV['RAILS_ENV'] = @environment
147:                                         Dir.chdir(@app_root)
148:                                         if @lower_privilege
149:                                                 lower_privilege('config/environment.rb', @lowest_user)
150:                                         end
151: 
152:                                         # require Rails' environment, using the same path as the original rails dispatcher,
153:                                         # which normally does: require File.dirname(__FILE__) + "/../config/environment"
154:                                         # thus avoiding the possibility of including the same file twice.
155:                                         require 'public/../config/environment'
156: 
157:                                         require 'dispatcher'
158:                                 end
159:                                 if success
160:                                         start_request_handler(channel, false)
161:                                 end
162:                         rescue SignalException => e
163:                                 if e.message != AbstractRequestHandler::HARD_TERMINATION_SIGNAL &&
164:                                    e.message != AbstractRequestHandler::SOFT_TERMINATION_SIGNAL
165:                                         raise
166:                                 end
167:                         end
168:                 end
169:                 b.close
170:                 Process.waitpid(pid) rescue nil
171:                 
172:                 channel = MessageChannel.new(a)
173:                 unmarshal_and_raise_errors(channel)
174:                 
175:                 # No exception was raised, so spawning succeeded.
176:                 pid, socket_name, socket_type = channel.read
177:                 if pid.nil?
178:                         raise IOError, "Connection closed"
179:                 end
180:                 owner_pipe = channel.recv_io
181:                 return Application.new(@app_root, pid, socket_name,
182:                         socket_type, owner_pipe)
183:         end
start()

Overrided from AbstractServer#start.

May raise these additional exceptions:

     # File lib/phusion_passenger/railz/application_spawner.rb, line 191
191:         def start
192:                 super
193:                 begin
194:                         unmarshal_and_raise_errors(server)
195:                 rescue IOError, SystemCallError, SocketError => e
196:                         stop
197:                         raise Error, "The application spawner server exited unexpectedly: #{e}"
198:                 rescue
199:                         stop
200:                         raise
201:                 end
202:         end