class Passenger::Railz::FrameworkSpawner

Starting it synchronously with AbstractServer#start_synchronously has not been tested.
Note: FrameworkSpawner may only be started asynchronously with AbstractServer#start.
FrameworkSpawner uses ApplicationSpawner internally.
RoR versions, use multiple FrameworkSpawner instances.
that you spawn through it must require the same RoR version. To handle multiple
framework version. So be careful when using FrameworkSpawner: the applications
A single FrameworkSpawner instance can only hold a single Ruby on Rails
before spawning the application instances.
quickly. This is done by preloading the Ruby on Rails framework into memory,
This class is capable of spawning Ruby on Rails application instances

def before_fork # :nodoc:

:nodoc:
Overrided method.
def before_fork # :nodoc:
	if GC.copy_on_write_friendly?
		# Garbage collect now so that the child process doesn't have to
		# do that (to prevent making pages dirty).
		GC.start
	end
end

def finalize_server # :nodoc:

:nodoc:
Overrided method.
def finalize_server # :nodoc:
	@spawners_lock.synchronize do
		@spawners_cond.signal
	end
	@spawners_cleaner.join
	@spawners.each_value do |spawner|
		spawner.stop
	end
end

def handle_reload(app_root = nil)

def handle_reload(app_root = nil)
	@spawners_lock.synchronize do
		if app_root.nil?
			@spawners.each_value do |spawner|
				spawner.stop
			end
			@spawners.clear
		else
			spawner = @spawners[app_root]
			if spawner
				spawner.stop
				@spawners.delete(app_root)
			end
		end
	end
end

def handle_spawn_application(app_root, lower_privilege, lowest_user, environment)

def handle_spawn_application(app_root, lower_privilege, lowest_user, environment)
	lower_privilege = lower_privilege == "true"
	@spawners_lock.synchronize do
		spawner = @spawners[app_root]
		if spawner.nil?
			begin
				spawner = ApplicationSpawner.new(app_root,
					lower_privilege, lowest_user,
					environment)
				spawner.start
			rescue ArgumentError, AppInitError, ApplicationSpawner::Error => e
				client.write('exception')
				client.write_scalar(marshal_exception(e))
				if e.child_exception.is_a?(LoadError)
					# A source file failed to load, maybe because of a
					# missing gem. If that's the case then the sysadmin
					# will install probably the gem. So we clear RubyGems's
					# cache so that it can detect new gems.
					Gem.clear_paths
				end
				return
			end
			@spawners[app_root] = spawner
		end
		spawner.time = Time.now
		begin
			app = spawner.spawn_application
		rescue ApplicationSpawner::Error => e
			spawner.stop
			@spawners.delete(app_root)
			client.write('exception')
			client.write_scalar(marshal_exception(e))
			return
		end
		client.write('success')
		client.write(app.pid, app.listen_socket_name, app.using_abstract_namespace?)
		client.send_io(app.owner_pipe)
		app.close
	end
end

def initialize(options = {})

then restart the server by calling AbstractServer#stop and AbstractServer#start.
of the FrameworkSpawner server. If you wish to reload the Rails framework's code,
Note that the specified Rails framework will be loaded during the entire life time

It is not allowed to specify both +version+ and +vendor+.

usually something like "/webapps/foo/vendor/rails".
- :vendor: The directory to the vendor Rails framework to use. This is
this version is actually installed.
- :version: The Ruby on Rails version to use. It is not checked whether
Valid options:

Creates a new instance of FrameworkSpawner.
def initialize(options = {})
	if !options.respond_to?(:'[]')
		raise ArgumentError, "The 'options' argument not seem to be an options hash"
	end
	@version = options[:version]
	@vendor = options[:vendor]
	if !@version && !@vendor
		raise ArgumentError, "Either the 'version' or the 'vendor' option must specified"
	elsif @version && @vendor
		raise ArgumentError, "It is not allowed to specify both the 'version' and the 'vendor' options"
	end
	
	super()
	define_message_handler(:spawn_application, :handle_spawn_application)
	define_message_handler(:reload, :handle_reload)
end

def initialize_server # :nodoc:

:nodoc:
Overrided method.
def initialize_server # :nodoc:
	$0 = "Passenger FrameworkSpawner: #{@version || @vendor}"
	@spawners = {}
	@spawners_lock = Mutex.new
	@spawners_cond = ConditionVariable.new
	@spawners_cleaner = Thread.new do
		begin
			spawners_cleaner_main_loop
		rescue Exception => e
			print_exception(self.class.to_s, e)
		end
	end
	begin
		preload_rails
	rescue StandardError, ScriptError, NoMemoryError => e
		client.write('exception')
		client.write_scalar(marshal_exception(e))
		return
	end
	client.write('success')
end

def preload_rails

def preload_rails
	Object.const_set(:RAILS_ROOT, ".")
	if @version
		gem 'rails', "=#{@version}"
		require 'initializer'
	else
		$LOAD_PATH.unshift("#{@vendor}/railties/builtin/rails_info")
		Dir["#{@vendor}/*"].each do |entry|
			next unless File.directory?(entry)
			$LOAD_PATH.unshift("#{entry}/lib")
		end
		require "#{@vendor}/railties/lib/initializer"
	end
	require 'active_support'
	require 'active_record'
	require 'action_controller'
	require 'action_view'
	require 'action_pack'
	require 'action_mailer'
	require 'dispatcher'
	begin
		if ::Rails::VERSION::MAJOR >= 2
			require 'active_resource'
		else
			require 'action_web_service'
		end
		require 'ruby_version_check'
		require 'active_support/whiny_nil'
	rescue NameError
		# Rails < 1.1
		require 'action_web_service'
	end
	Object.send(:remove_const, :RAILS_ROOT)
end

def reload(app_root = nil)

- FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
application root.
- ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails
Raises:

code will be freshly loaded into memory.
that the next time an application instance is spawned, the application
This method makes sure that any cached application code is removed, so
application, such as after deploying a new version of the application.
once it a while, it will be necessary to reload the code for an
Application code might be cached in memory by a FrameworkSpawner. But
Long description:

instances will be removed, no matter the application root.
If nil is specified as application root, then all cached application
Remove the cached application instances at the given application root.
def reload(app_root = nil)
	if app_root.nil?
		server.write("reload")
	else
		server.write("reload", normalize_path(app_root))
	end
rescue SystemCallError, IOError, SocketError
	raise Error, "The framework spawner server exited unexpectedly"
end

def spawn_application(app_root, lower_privilege = true, lowest_user = "nobody", environment = "production")

- FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
- ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
- AppInitError: The application raised an exception or called exit() during startup.
- ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
- AbstractServer::ServerNotStarted: The FrameworkSpawner server hasn't already been started.
Raises:

- Reload the application by calling reload with the correct app_root argument.
- Restart this FrameworkSpawner by calling AbstractServer#stop, then AbstractServer#start.
the application's code, you must do one of these things:
speed up future spawning attempts. This implies that, if you've changed
FrameworkSpawner will internally cache the code of applications, in order to

+lowest_user+ and +environment+ parameters.
See ApplicationSpawner.new for an explanation of the +lower_privilege+,

the spawned RoR application.
When successful, an Application object will be returned, which represents
version associated with this FrameworkSpawner.
Spawn a RoR application using the Ruby on Rails framework
def spawn_application(app_root, lower_privilege = true, lowest_user = "nobody", environment = "production")
	app_root = normalize_path(app_root)
	assert_valid_app_root(app_root)
	exception_to_propagate = nil
	begin
		server.write("spawn_application", app_root, lower_privilege, lowest_user, environment)
		result = server.read
		if result.nil?
			raise IOError, "Connection closed"
		end
		if result[0] == 'exception'
			raise unmarshal_exception(server.read_scalar)
		else
			pid, listen_socket_name, using_abstract_namespace = server.read
			if pid.nil?
				raise IOError, "Connection closed"
			end
			owner_pipe = server.recv_io
			return Application.new(app_root, pid, listen_socket_name,
				using_abstract_namespace == "true", owner_pipe)
		end
	rescue SystemCallError, IOError, SocketError => e
		raise Error, "The framework spawner server exited unexpectedly"
	end
end

def spawners_cleaner_main_loop

APP_SPAWNER_MAX_IDLE_TIME seconds.
and stops application spawners that have been idle for more than
This thread checks the spawners list every APP_SPAWNER_CLEAN_INTERVAL seconds,
The main loop for the spawners cleaner thread.
def spawners_cleaner_main_loop
	@spawners_lock.synchronize do
		while true
			if @spawners_cond.timed_wait(@spawners_lock, APP_SPAWNER_CLEAN_INTERVAL)
				break
			else
				current_time = Time.now
				@spawners.keys.each do |key|
					spawner = @spawners[key]
					if current_time - spawner.time > APP_SPAWNER_MAX_IDLE_TIME
						spawner.stop
						@spawners.delete(key)
					end
				end
			end
		end
	end
end

def start

- FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
- FrameworkInitError: The specified Ruby on Rails framework could not be loaded.
May raise these additional exceptions:

Overrided from AbstractServer#start.
def start
	super
	begin
		status = server.read[0]
		if status == 'exception'
			child_exception = unmarshal_exception(server.read_scalar)
			stop
			if @version
				message = "Could not load Ruby on Rails framework version #{@version}: " <<
					"#{child_exception.class} (#{child_exception.message})"
			else
				message = "Could not load Ruby on Rails framework at '#{@vendor}': " <<
					"#{child_exception.class} (#{child_exception.message})"
			end
			options = { :vendor => @vendor, :version => @version }
			raise FrameworkInitError.new(message, child_exception, options)
		end
	rescue IOError, SystemCallError, SocketError
		stop
		raise Error, "The framework spawner server exited unexpectedly"
	end
end