class PhusionPassenger::SpawnManager

it’s to work around an obscure bug in ActiveSupport’s Dispatcher.
In case you’re wondering why the namespace is “ClassicRails” and not “Rails”:
ClassicRails::ApplicationSpawner to preload and cache application code.
Ruby on Rails frameworks. ClassicRails::FrameworkSpawner, in turn, uses
Internally, SpawnManager uses ClassicRails::FrameworkSpawner to preload and cache
code, so subsequent spawns will be very fast.
will preload and cache Ruby on Rails frameworks, as well as application
Spawning a Ruby on Rails application is usually slow. But SpawnManager
== Ruby on Rails optimizations
finished.
tested. Don’t forget to call cleanup after the server’s main loop has
AbstractServer#start_synchronously. Starting asynchronously has not been
Note: SpawnManager may only be started synchronously with
system.
instances. It acts like a simple fascade for the rest of the spawn manager
The spawn manager is capable of spawning Ruby on Rails or Rack application

def app_name(app_type)

def app_name(app_type)
	if app_type == "rails"
		return "Ruby on Rails"
	else
		return "Ruby (Rack)"
	end
end

def cleanup

Cleanup resources. Should be called when this SpawnManager is no longer needed.
def cleanup
	@spawners.cleanup
end

def database_error?(e)

def database_error?(e)
	return ( defined?(Mysql::Error) && e.child_exception.is_a?(Mysql::Error) ) ||
	       ( e.child_exception.is_a?(UnknownError) &&
	           (
	               e.child_exception.real_class_name =~ /^ActiveRecord/ ||
	               e.child_exception.real_class_name =~ /^Mysql::/
	           )
	       )
end

def handle_reload(client, app_group_name)

def handle_reload(client, app_group_name)
	reload(app_group_name)
end

def handle_spawn_application(client, *options)

def handle_spawn_application(client, *options)
	options     = sanitize_spawn_options(Hash[*options])
	app_process = nil
	app_root    = options["app_root"]
	app_type    = options["app_type"]
	begin
		app_process = spawn_application(options)
	rescue AbstractServer::ServerError => e
		send_error_page(client, 'general_error', :error => e)
	rescue VersionNotFound => e
		send_error_page(client, 'version_not_found', :error => e, :app_root => app_root)
	rescue AppInitError => e
		if database_error?(e)
			send_error_page(client, 'database_error', :error => e,
				:app_root => app_root, :app_name => app_name(app_type),
				:app_type => app_type)
		elsif load_error?(e)
			# 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
			send_error_page(client, 'load_error', :error => e, :app_root => app_root,
				:app_name => app_name(app_type))
		elsif e.child_exception.is_a?(SystemExit)
			send_error_page(client, 'app_exited_during_initialization', :error => e,
				:app_root => app_root, :app_name => app_name(app_type))
		else
			send_error_page(client, 'app_init_error', :error => e,
				:app_root => app_root, :app_name => app_name(app_type))
		end
	rescue FrameworkInitError => e
		send_error_page(client, 'framework_init_error', :error => e)
	end
	if app_process
		begin
			client.write('ok')
			app_process.write_to_channel(client)
		rescue Errno::EPIPE
			# The Apache module may be interrupted during a spawn command,
			# in which case it will close the connection. We ignore this error.
		ensure
			app_process.close
		end
	end
end

def initialize(options = {})

def initialize(options = {})
	super("", "")
	@options = options
	@spawners = AbstractServerCollection.new
	define_message_handler(:spawn_application, :handle_spawn_application)
	define_message_handler(:reload, :handle_reload)
	define_signal_handler('SIGHUP', :reload)
	
	# Start garbage collector in order to free up some existing
	# heap slots. This prevents the heap from growing unnecessarily
	# during the startup phase.
	GC.start
	if GC.copy_on_write_friendly?
		# Preload libraries for copy-on-write semantics.
		require 'base64'
		require 'phusion_passenger/app_process'
		require 'phusion_passenger/classic_rails/framework_spawner'
		require 'phusion_passenger/classic_rails/application_spawner'
		require 'phusion_passenger/rack/application_spawner'
		require 'phusion_passenger/html_template'
		require 'phusion_passenger/platform_info'
		require 'phusion_passenger/exceptions'
	end
end

def load_error?(e)

def load_error?(e)
	return e.child_exception.is_a?(LoadError) || (
	           e.child_exception.is_a?(UnknownError) &&
	           e.child_exception.real_class_name == "MissingSourceFile"
	)
end

def reload(app_group_name = nil)

Raises AbstractServer::SpawnError if something went wrong.

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

instances will be removed, no matter the group name.
If nil is specified as group name, then all cached application
Remove the cached application instances at the given group name.
def reload(app_group_name = nil)
	@spawners.synchronize do
		if app_group_name
			# Stop and delete associated ApplicationSpawner.
			@spawners.delete("app:#{app_group_name}")
			# Propagate reload command to associated FrameworkSpawner.
			@spawners.each do |spawner|
				if spawner.respond_to?(:reload)
					spawner.reload(app_group_name)
				end
			end
		else
			# Stop and delete all spawners.
			@spawners.clear
		end
	end
end

def send_error_page(channel, template_name, options = {})

def send_error_page(channel, template_name, options = {})
	require 'phusion_passenger/html_template' unless defined?(HTMLTemplate)
	if !defined?(PlatformInfo)
		require 'phusion_passenger/platform_info'
		require 'phusion_passenger/platform_info/ruby'
	end
	options["enterprisey"] = File.exist?("#{SOURCE_ROOT}/enterprisey.txt") ||
		File.exist?("/etc/passenger_enterprisey.txt")
	data = HTMLTemplate.new(template_name, options).result
	channel.write('error_page')
	channel.write_scalar(data)
end

def spawn_application(options)

- AppInitError: The application raised an exception or called exit() during startup.
- FrameworkInitError: The Ruby on Rails framework that the application requires could not be loaded.
- AbstractServer::ServerError: One of the server processes exited unexpectedly.
is not installed.
- VersionNotFound: The Ruby on Rails framework version that the given application requires
- InvalidPath: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
Exceptions:

- 'print_exceptions'
- 'base_uri'
data must be a NULL.
key-value pairs, encoded in base64. The last byte in the unencoded
to the spawned application process. This is NULL-seperated string of
- 'environment_variables': Environment variables which should be passed
- 'app_spawner_timeout'
- 'framework_spawner_timeout'
- 'default_group'
- 'default_user'
- 'group'
- 'user',
- 'spawn_method'
- 'environment'
- 'app_type'
Optional options:

- 'app_root'
Mandatory options:

Most options are explained in PoolOptions.h.

process.
AppProcess object will be returned, which represents the spawned application
Spawns an application with the given spawn options. When successful, an
def spawn_application(options)
	if !options["app_root"]
		raise ArgumentError, "The 'app_root' option must be given."
	end
	options = sanitize_spawn_options(options)
	
	case options["app_type"]
	when "rails"
		if !defined?(ClassicRails::FrameworkSpawner)
			require 'phusion_passenger/classic_rails/framework_spawner'
			require 'phusion_passenger/classic_rails/application_spawner'
		end
		return spawn_rails_application(options)
	when "rack"
		if !defined?(Rack::ApplicationSpawner)
			require 'phusion_passenger/rack/application_spawner'
		end
		return spawn_rack_application(options)
	when "wsgi"
		if !defined?(WSGI::ApplicationSpawner)
			require 'phusion_passenger/wsgi/application_spawner'
		end
		return WSGI::ApplicationSpawner.spawn_application(options)
	else
		raise ArgumentError, "Unknown 'app_type' value '#{options["app_type"]}'."
	end
end

def spawn_rack_application(options)

def spawn_rack_application(options)
	app_group_name = options["app_group_name"]
	spawn_method   = options["spawn_method"]
	spawner        = nil
	create_spawner = nil
	key            = nil
	
	case spawn_method
	when nil, "", "smart", "smart-lv2"
		@spawners.synchronize do
			key = "app:#{app_group_name}"
			spawner = @spawners.lookup_or_add(key) do
				spawner_timeout = options["app_spawner_timeout"]
				spawner = Rack::ApplicationSpawner.new(
					@options.merge(options))
				if spawner_timeout != -1
					spawner.max_idle_time = spawner_timeout
				end
				spawner.start
				spawner
			end
			begin
				return spawner.spawn_application(options)
			rescue AbstractServer::ServerError
				@spawners.delete(key)
				raise
			end
		end
	else
		return Rack::ApplicationSpawner.spawn_application(
			@options.merge(options))
	end
end

def spawn_rails_application(options)

def spawn_rails_application(options)
	app_root       = options["app_root"]
	app_group_name = options["app_group_name"]
	spawn_method   = options["spawn_method"]
	spawner        = nil
	create_spawner = nil
	key            = nil
	
	case spawn_method
	when nil, "", "smart", "smart-lv2"
		if spawn_method != "smart-lv2"
			framework_version = AppProcess.detect_framework_version(app_root)
		end
		if framework_version.nil? || framework_version == :vendor
			key = "app:#{app_group_name}"
			create_spawner = proc do
				ClassicRails::ApplicationSpawner.new(@options.merge(options))
			end
			spawner_timeout = options["app_spawner_timeout"]
		else
			key = "version:#{framework_version}"
			create_spawner = proc do
				options["framework_version"] = framework_version
				ClassicRails::FrameworkSpawner.new(@options.merge(options))
			end
			spawner_timeout = options["framework_spawner_timeout"]
		end
		
		@spawners.synchronize do
			spawner = @spawners.lookup_or_add(key) do
				spawner = create_spawner.call
				if spawner_timeout != -1
					spawner.max_idle_time = spawner_timeout
				end
				spawner.start
				spawner
			end
			begin
				return spawner.spawn_application(options)
			rescue AbstractServer::ServerError
				@spawners.delete(key)
				raise
			end
		end
	else
		return ClassicRails::ApplicationSpawner.spawn_application(
			@options.merge(options))
	end
end