This repository is private.
All pages are served over SSL and all pushing and pulling is done over SSH.
No one may fork, clone, or view it unless they are added as a member.
Every repository with this icon (
) is private.
Every repository with this icon (
This repository is public.
Anyone may fork, clone, or view it.
Every repository with this icon (
) is public.
Every repository with this icon (
Hongli Lai (Phusion) (author)
Wed May 07 10:29:13 -0700 2008
| bd6e3b45 » | Hongli Lai (Phusion) | 2008-04-03 | 1 | # Phusion Passenger - http://www.modrails.com/ | |
| 2 | # Copyright (C) 2008 Phusion | ||||
| 3 | # | ||||
| 4 | # This program is free software; you can redistribute it and/or modify | ||||
| 5 | # it under the terms of the GNU General Public License as published by | ||||
| 6 | # the Free Software Foundation; version 2 of the License. | ||||
| 7 | # | ||||
| 8 | # This program is distributed in the hope that it will be useful, | ||||
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| 11 | # GNU General Public License for more details. | ||||
| 12 | # | ||||
| 13 | # You should have received a copy of the GNU General Public License along | ||||
| 14 | # with this program; if not, write to the Free Software Foundation, Inc., | ||||
| 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
| 16 | |||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 17 | require 'rubygems' | |
| 18 | require 'socket' | ||||
| 19 | require 'pathname' | ||||
| 9d714c0d » | Hongli Lai (Phusion) | 2008-03-01 | 20 | require 'passenger/abstract_server' | |
| 21 | require 'passenger/application_spawner' | ||||
| 22 | require 'passenger/utils' | ||||
| 16201608 » | Hongli Lai (Phusion) | 2008-03-01 | 23 | module Passenger | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 24 | ||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 25 | # Raised when FrameworkSpawner or SpawnManager was unable to load a version of | |
| a7d4be71 » | Hongli Lai (Phusion) | 2008-03-07 | 26 | # the Ruby on Rails framework. The +child_exception+ attribute is guaranteed | |
| 27 | # non-nil. | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 28 | class FrameworkInitError < InitializationError | |
| a7d4be71 » | Hongli Lai (Phusion) | 2008-03-07 | 29 | attr_reader :vendor | |
| 30 | attr_reader :version | ||||
| 31 | |||||
| 32 | def initialize(message, child_exception, options) | ||||
| 33 | super(message, child_exception) | ||||
| 34 | if options[:vendor] | ||||
| 35 | @vendor = options[:vendor] | ||||
| 36 | else | ||||
| 37 | @version = options[:version] | ||||
| 38 | end | ||||
| 39 | end | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 40 | end | |
| 41 | |||||
| 2c9448a9 » | Hongli Lai (Phusion) | 2008-03-02 | 42 | # This class is capable of spawning Ruby on Rails application instances | |
| 43 | # quickly. This is done by preloading the Ruby on Rails framework into memory, | ||||
| 44 | # before spawning the application instances. | ||||
| 45 | # | ||||
| 46 | # A single FrameworkSpawner instance can only hold a single Ruby on Rails | ||||
| 47 | # framework version. So be careful when using FrameworkSpawner: the applications | ||||
| 48 | # that you spawn through it must require the same RoR version. To handle multiple | ||||
| 49 | # RoR versions, use multiple FrameworkSpawner instances. | ||||
| 50 | # | ||||
| 51 | # FrameworkSpawner uses ApplicationSpawner internally. | ||||
| 52 | # | ||||
| 53 | # *Note*: FrameworkSpawner may only be started asynchronously with AbstractServer#start. | ||||
| 54 | # Starting it synchronously with AbstractServer#start_synchronously has not been tested. | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 55 | class FrameworkSpawner < AbstractServer | |
| 56 | APP_SPAWNER_MAX_IDLE_TIME = 120 | ||||
| 82065691 » | Hongli Lai (Phusion) | 2008-03-31 | 57 | APP_SPAWNER_CLEAN_INTERVAL = APP_SPAWNER_MAX_IDLE_TIME + 5 | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 58 | ||
| 59 | include Utils | ||||
| 412648e0 » | Hongli Lai (Phusion) | 2008-01-25 | 60 | ||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 61 | # This exception means that the FrameworkSpawner server process exited unexpectedly. | |
| 62 | class Error < AbstractServer::ServerError | ||||
| 63 | end | ||||
| 64 | |||||
| 16201608 » | Hongli Lai (Phusion) | 2008-03-01 | 65 | # An attribute, used internally. This should not be used outside Passenger. | |
| 412648e0 » | Hongli Lai (Phusion) | 2008-01-25 | 66 | attr_accessor :time | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 67 | ||
| 68 | # Creates a new instance of FrameworkSpawner. | ||||
| 69 | # | ||||
| c296409f » | Hongli Lai (Phusion) | 2008-02-27 | 70 | # Valid options: | |
| 71 | # - <tt>:version</tt>: The Ruby on Rails version to use. It is not checked whether | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 72 | # this version is actually installed. | |
| c296409f » | Hongli Lai (Phusion) | 2008-02-27 | 73 | # - <tt>:vendor</tt>: The directory to the vendor Rails framework to use. This is | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 74 | # usually something like "/webapps/foo/vendor/rails". | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 75 | # | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 76 | # It is not allowed to specify both +version+ and +vendor+. | |
| c296409f » | Hongli Lai (Phusion) | 2008-02-27 | 77 | # | |
| 78 | # Note that the specified Rails framework will be loaded during the entire life time | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 79 | # of the FrameworkSpawner server. If you wish to reload the Rails framework's code, | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 80 | # then restart the server by calling AbstractServer#stop and AbstractServer#start. | |
| c296409f » | Hongli Lai (Phusion) | 2008-02-27 | 81 | def initialize(options = {}) | |
| 16201608 » | Hongli Lai (Phusion) | 2008-03-01 | 82 | if !options.respond_to?(:'[]') | |
| 83 | raise ArgumentError, "The 'options' argument not seem to be an options hash" | ||||
| 84 | end | ||||
| c296409f » | Hongli Lai (Phusion) | 2008-02-27 | 85 | @version = options[:version] | |
| 86 | @vendor = options[:vendor] | ||||
| 16201608 » | Hongli Lai (Phusion) | 2008-03-01 | 87 | if !@version && !@vendor | |
| 88 | raise ArgumentError, "Either the 'version' or the 'vendor' option must specified" | ||||
| 89 | elsif @version && @vendor | ||||
| 90 | raise ArgumentError, "It is not allowed to specify both the 'version' and the 'vendor' options" | ||||
| 91 | end | ||||
| 92 | |||||
| 93 | super() | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 94 | define_message_handler(:spawn_application, :handle_spawn_application) | |
| 95 | define_message_handler(:reload, :handle_reload) | ||||
| 96 | end | ||||
| 97 | |||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 98 | # Overrided from AbstractServer#start. | |
| 99 | # | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 100 | # May raise these additional exceptions: | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 101 | # - FrameworkInitError: The specified Ruby on Rails framework could not be loaded. | |
| 102 | # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly. | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 103 | def start | |
| 104 | super | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 105 | begin | |
| 106 | status = server.read[0] | ||||
| 107 | if status == 'exception' | ||||
| 108 | child_exception = unmarshal_exception(server.read_scalar) | ||||
| 109 | stop | ||||
| 110 | if @version | ||||
| 111 | message = "Could not load Ruby on Rails framework version #{@version}: " << | ||||
| 112 | "#{child_exception.class} (#{child_exception.message})" | ||||
| 113 | else | ||||
| 114 | message = "Could not load Ruby on Rails framework at '#{@vendor}': " << | ||||
| 115 | "#{child_exception.class} (#{child_exception.message})" | ||||
| 116 | end | ||||
| a7d4be71 » | Hongli Lai (Phusion) | 2008-03-07 | 117 | options = { :vendor => @vendor, :version => @version } | |
| 118 | raise FrameworkInitError.new(message, child_exception, options) | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 119 | end | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 120 | rescue IOError, SystemCallError, SocketError | |
| 121 | stop | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 122 | raise Error, "The framework spawner server exited unexpectedly" | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 123 | end | |
| 124 | end | ||||
| 125 | |||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 126 | # Spawn a RoR application using the Ruby on Rails framework | |
| 127 | # version associated with this FrameworkSpawner. | ||||
| 128 | # When successful, an Application object will be returned, which represents | ||||
| 129 | # the spawned RoR application. | ||||
| 130 | # | ||||
| fb13dec0 » | Hongli Lai (Phusion) | 2008-05-07 | 131 | # See ApplicationSpawner.new for an explanation of the +lower_privilege+, | |
| 132 | # +lowest_user+ and +environment+ parameters. | ||||
| b2d4cc2f » | Hongli Lai (Phusion) | 2008-02-23 | 133 | # | |
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 134 | # FrameworkSpawner will internally cache the code of applications, in order to | |
| 135 | # speed up future spawning attempts. This implies that, if you've changed | ||||
| 136 | # the application's code, you must do one of these things: | ||||
| 137 | # - Restart this FrameworkSpawner by calling AbstractServer#stop, then AbstractServer#start. | ||||
| 138 | # - Reload the application by calling reload with the correct app_root argument. | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 139 | # | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 140 | # Raises: | |
| 141 | # - AbstractServer::ServerNotStarted: The FrameworkSpawner server hasn't already been started. | ||||
| 142 | # - ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails application root. | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 143 | # - AppInitError: The application raised an exception or called exit() during startup. | |
| 144 | # - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly. | ||||
| 145 | # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly. | ||||
| fb13dec0 » | Hongli Lai (Phusion) | 2008-05-07 | 146 | def spawn_application(app_root, lower_privilege = true, lowest_user = "nobody", environment = "production") | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 147 | app_root = normalize_path(app_root) | |
| 148 | assert_valid_app_root(app_root) | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 149 | exception_to_propagate = nil | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 150 | begin | |
| fb13dec0 » | Hongli Lai (Phusion) | 2008-05-07 | 151 | server.write("spawn_application", app_root, lower_privilege, lowest_user, environment) | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 152 | result = server.read | |
| 153 | if result.nil? | ||||
| 154 | raise IOError, "Connection closed" | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 155 | end | |
| 156 | if result[0] == 'exception' | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 157 | raise unmarshal_exception(server.read_scalar) | |
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 158 | else | |
| 159 | pid, listen_socket_name, using_abstract_namespace = server.read | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 160 | if pid.nil? | |
| 161 | raise IOError, "Connection closed" | ||||
| 162 | end | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 163 | owner_pipe = server.recv_io | |
| 164 | return Application.new(app_root, pid, listen_socket_name, | ||||
| 165 | using_abstract_namespace == "true", owner_pipe) | ||||
| 166 | end | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 167 | rescue SystemCallError, IOError, SocketError => e | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 168 | raise Error, "The framework spawner server exited unexpectedly" | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 169 | end | |
| 170 | end | ||||
| 171 | |||||
| 763b9371 » | Hongli Lai (Phusion) | 2008-02-26 | 172 | # Remove the cached application instances at the given application root. | |
| 173 | # If nil is specified as application root, then all cached application | ||||
| 174 | # instances will be removed, no matter the application root. | ||||
| 175 | # | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 176 | # <b>Long description:</b> | |
| 763b9371 » | Hongli Lai (Phusion) | 2008-02-26 | 177 | # Application code might be cached in memory by a FrameworkSpawner. But | |
| 178 | # once it a while, it will be necessary to reload the code for an | ||||
| 179 | # application, such as after deploying a new version of the application. | ||||
| 180 | # This method makes sure that any cached application code is removed, so | ||||
| 181 | # that the next time an application instance is spawned, the application | ||||
| 182 | # code will be freshly loaded into memory. | ||||
| 183 | # | ||||
| 98c09711 » | Hongli Lai (Phusion) | 2008-03-03 | 184 | # Raises: | |
| 185 | # - ArgumentError: +app_root+ doesn't appear to be a valid Ruby on Rails | ||||
| 186 | # application root. | ||||
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 187 | # - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly. | |
| 412648e0 » | Hongli Lai (Phusion) | 2008-01-25 | 188 | def reload(app_root = nil) | |
| 189 | if app_root.nil? | ||||
| ccd01b56 » | Hongli Lai (Phusion) | 2008-02-23 | 190 | server.write("reload") | |
| 412648e0 » | Hongli Lai (Phusion) | 2008-01-25 | 191 | else | |
| ccd01b56 » | Hongli Lai (Phusion) | 2008-02-23 | 192 | server.write("reload", normalize_path(app_root)) | |
| 412648e0 » | Hongli Lai (Phusion) | 2008-01-25 | 193 | end | |
| 763b9371 » | Hongli Lai (Phusion) | 2008-02-26 | 194 | rescue SystemCallError, IOError, SocketError | |
| cec5c76b » | Hongli Lai (Phusion) | 2008-03-07 | 195 | raise Error, "The framework spawner server exited unexpectedly" | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 196 | end | |
| 197 | |||||
| 198 | protected | ||||
| 199 | # Overrided method. | ||||
| d73f7e27 » | Hongli Lai (Phusion) | 2008-02-22 | 200 | def before_fork # :nodoc: | |
| b28fbb8f » | Hongli Lai (Phusion) | 2008-03-17 | 201 | if GC.copy_on_write_friendly? | |
| 202 | # Garbage collect now so that the child process doesn't have to | ||||
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 203 | # do that (to prevent making pages dirty). | |
| 204 | GC.start | ||||
| 205 | end | ||||
| 206 | end | ||||
| 207 | |||||
| 208 | # Overrided method. | ||||
| d73f7e27 » | Hongli Lai (Phusion) | 2008-02-22 | 209 | def initialize_server # :nodoc: | |
| 1e21c0e7 » | Hongli Lai (Phusion) | 2008-02-29 | 210 | $0 = "Passenger FrameworkSpawner: #{@version || @vendor}" | |
| be0f3b4e » | Hongli Lai (Phusion) | 2008-01-25 | 211 | @spawners = {} | |
| 212 | @spawners_lock = Mutex.new | ||||
| 213 | @spawners_cond = ConditionVariable.new | ||||
| 214 | @spawners_cleaner = Thread.new do | ||||
| 215 | begin | ||||
| 216 | spawners_cleaner_main_loop | ||||
| 217 | rescue Exception => e | ||||
| 218 | print_exception(self.class.to_s, e) | ||||
| 219 | end | ||||
| 220 | end | ||||
| d21001ab » | Hongli Lai (Phusion) | 2008-03-03 | 221 | begin | |
| 222 | preload_rails | ||||
| 223 | rescue StandardError, ScriptError, NoMemoryError => e | ||||
| 224 | client.write('exception') | ||||
| 225 | client.write_scalar(marshal_exception(e)) | ||||
| 226 | return | ||||
| 227 | end | ||||
| 228 | client.write( | ||||





