about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorzedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9>2006-02-28 07:04:41 +0000
committerzedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9>2006-02-28 07:04:41 +0000
commitd1a01c03f71c14e5d4fe66d93c5ab444c0aba554 (patch)
treed7d4df48f1e708dfceea7bd78907177e5b1be889
parent4e5132f63a210beb766ebfe52bea7424903403ae (diff)
downloadunicorn-d1a01c03f71c14e5d4fe66d93c5ab444c0aba554.tar.gz
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@65 19e92222-5c0b-0410-8929-a290d50e31e9
-rw-r--r--bin/mongrel_rails51
-rw-r--r--bin/mongrel_rails_service14
-rw-r--r--bin/mongrel_rails_svc102
-rw-r--r--lib/mongrel.rb18
-rw-r--r--lib/mongrel/command.rb38
-rw-r--r--lib/mongrel/plugins.rb78
-rw-r--r--lib/mongrel/rails.rb68
-rw-r--r--lib/pluginfactory.rb384
-rw-r--r--test/plugins/commands/test1.rb8
-rw-r--r--test/test_plugins.rb11
10 files changed, 224 insertions, 548 deletions
diff --git a/bin/mongrel_rails b/bin/mongrel_rails
index 9d58628..c9051fe 100644
--- a/bin/mongrel_rails
+++ b/bin/mongrel_rails
@@ -1,47 +1,9 @@
 require 'rubygems'
-require 'mongrel'
-require 'mongrel/command'
+require 'mongrel/rails'
 
 
-class RailsHandler < Mongrel::HttpHandler
-
-  def initialize(dir, mime_map = {})
-    @files = Mongrel::DirHandler.new(dir,false)
-    @guard = Mutex.new
-    
-    # register the requested mime types
-    mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
-  end
-  
-  def process(request, response)
-    # not static, need to talk to rails
-    return if response.socket.closed?
-
-    if @files.can_serve(request.params["PATH_INFO"])
-      @files.process(request,response)
-    else
-      cgi = Mongrel::CGIWrapper.new(request, response)
-
-      begin
-        @guard.synchronize do
-          # Rails is not thread safe so must be run entirely within synchronize
-          Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
-        end
-
-        # This finalizes the output using the proper HttpResponse way
-        cgi.out {""}
-      rescue Object => rails_error
-        STDERR.puts "calling Dispatcher.dispatch #{rails_error}"
-        STDERR.puts rails_error.backtrace.join("\n")
-      end
-    end
-
-  end
-end
-
-
-
-class StartCommand < Mongrel::Command::Command
+class Start < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
 
   def configure
     options [
@@ -177,7 +139,9 @@ def send_signal(signal, pid_file)
   puts "Done."
 end
 
-class StopCommand < Mongrel::Command::Command
+
+class Stop < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
 
   def configure
     options [
@@ -211,7 +175,8 @@ end
 
 
 
-class RestartCommand < Mongrel::Command::Command
+class Restart < Mongrel::Plugin "/commands"
+    include Mongrel::Command::Command
 
   def configure
     options [
diff --git a/bin/mongrel_rails_service b/bin/mongrel_rails_service
index 50131ae..352219c 100644
--- a/bin/mongrel_rails_service
+++ b/bin/mongrel_rails_service
@@ -7,8 +7,6 @@
 ###############################################
 require 'rubygems'
 require 'mongrel'
-require 'mongrel/command'
-
 require 'win32/service'
 include Win32
 
@@ -32,7 +30,8 @@ module GenericCommand
   end
 end
 
-class InstallCommand < Mongrel::Command::Command
+class InstallCommand < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
 
   # Default every option to nil so only the defined ones get passed to service
   # (which will override ServiceCommand defaults).
@@ -175,7 +174,8 @@ class InstallCommand < Mongrel::Command::Command
   end
 end
 
-class DeleteCommand < Mongrel::Command::Command
+class Delete < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
   include GenericCommand
 
   def run
@@ -193,7 +193,8 @@ class DeleteCommand < Mongrel::Command::Command
   end
 end
 
-class StartCommand < Mongrel::Command::Command
+class Start < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
   include GenericCommand
   
   def run
@@ -217,7 +218,8 @@ class StartCommand < Mongrel::Command::Command
   end
 end
 
-class StopCommand < Mongrel::Command::Command
+class Stop < Mongrel::Plugin "/commands"
+  include Mongrel::Command::Command
   include GenericCommand
   
   def run
diff --git a/bin/mongrel_rails_svc b/bin/mongrel_rails_svc
index bc65d66..5e19e82 100644
--- a/bin/mongrel_rails_svc
+++ b/bin/mongrel_rails_svc
@@ -4,10 +4,8 @@
 # This is where Win32::Daemon resides.
 ###############################################
 require 'rubygems'
-require 'mongrel'
-
+require 'mongrel/rails'
 require 'optparse'
-
 require 'win32/service'
 
 # We need to use OpenProcess and SetProcessAffinityMask on WinNT/2K/XP for
@@ -43,39 +41,12 @@ end
 DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log')
 DEBUG_THREAD_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug_thread.log')
 
-class RailsHandler < Mongrel::HttpHandler
-  def initialize(dir, mime_map = {})
-    @files = Mongrel::DirHandler.new(dir,false)
-    @guard = Mutex.new
-    
-    # register the requested mime types
-    mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
-  end
+def dbg(msg)
+  File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
+end
   
-  def process(request, response)
-    # not static, need to talk to rails
-    return if response.socket.closed?
-
-    if @files.can_serve(request.params["PATH_INFO"])
-      @files.process(request,response)
-    else
-      cgi = Mongrel::CGIWrapper.new(request, response)
-
-      begin
-        @guard.synchronize do
-          # Rails is not thread safe so must be run entirely within synchronize
-          Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
-        end
-
-        # This finalizes the output using the proper HttpResponse way
-        cgi.out {""}
-      rescue Object => rails_error
-        STDERR.puts "calling Dispatcher.dispatch #{rails_error}"
-        STDERR.puts rails_error.backtrace.join("\n")
-      end
-    end
-  end
-
+def dbg_th(msg)
+  File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }  
 end
 
 # This class encapsulate the handler registering, http_server and working thread
@@ -83,7 +54,7 @@ end
 # (in case you don't want use mongrel_rails script)
 class MongrelRails
   def initialize(ip, port, rails_root, docroot, environment, mime_map, num_procs, timeout)
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - mongrelrails_initialize entered") }
+    dbg "mongrelrails_initialize entered"
 
     @ip = ip
     @port = port
@@ -94,24 +65,23 @@ class MongrelRails
     @num_procs = num_procs
     @timeout = timeout
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - mongrelrails_initialize left") }
+    dbg "mongrelrails_initialize left"
   end
   
   def delayed_initialize
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - delayed_initialize entered") }
+    dbg "delayed_initialize entered"
 
     @rails = configure_rails
-    #@rails = SimpleHandler.new
     
     # start up mongrel with the right configurations
     @server = Mongrel::HttpServer.new(@ip, @port, @num_procs.to_i, @timeout.to_i)
     @server.register("/", @rails)
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - delayed_initialize left") }
+    dbg "delayed_initialize left"
   end
   
   def load_mime_map
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map entered") }
+    dbg "load_mime_map entered"
 
     mime = {}
 
@@ -124,13 +94,13 @@ class MongrelRails
       mime.each {|k,v| puts "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
     end
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map left") }
+    dbg "load_mime_map left"
     
     return mime
   end
 
   def configure_rails
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails entered") }
+    dbg "configure_rails entered"
 
     Dir.chdir(@rails_root)
 
@@ -140,85 +110,85 @@ class MongrelRails
     # configure the rails handler
     rails = RailsHandler.new(@docroot, load_mime_map)
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails left") }
+    dbg "configure_rails left"
 
     return rails
   end
 
   def start_serve
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - start_serve entered") }
+    dbg "start_serve entered"
     
     @runner = Thread.new do
-      File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - runner_thread suspended") }
+      dbg_th "runner_thread suspended"
       Thread.stop
       
-      File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - runner_thread resumed") }
-      File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - runner_thread acceptor.join") }
+      dbg_th "runner_thread resumed"
+      dbg_th "runner_thread acceptor.join"
       @server.acceptor.join
     end
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - server.run") }
+    dbg "server.run"
     @server.run
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - runner.run") }
+    dbg "runner.run"
     @runner.run
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - start_serve left") }    
+    dbg "start_serve left"    
   end
   
   def stop_serve
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - stop_serve entered") }
+    dbg "stop_serve entered"
 
     if @runner.alive?
-      File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - killing thread") }
+      dbg "killing thread"
       @runner.kill
     end
     
     @server.stop
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - stop_serve left") }
+    dbg "stop_serve left"
   end
 end
 
 class RailsDaemon < Win32::Daemon
   def initialize(rails)
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize entered") }
+    dbg "daemon_initialize entered"
 
     @rails = rails
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize left") }
+    dbg "daemon_initialize left"
   end
 
   def service_init
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init entered") }
+    dbg "service_init entered"
     
     @rails.delayed_initialize
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init left") }    
+    dbg "service_init left"    
   end
   
   def service_main
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main entered") }
+    dbg "service_main entered"
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - rails.start_serve") }
+    dbg "rails.start_serve"
     @rails.start_serve
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - while RUNNING") }
+    dbg "while RUNNING"
     while state == RUNNING
       sleep 1
     end
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - state !RUNNING") }
+    dbg "state !RUNNING"
 
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - rails.stop_serve") }
+    dbg "rails.stop_serve"
     @rails.stop_serve
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main left") }
+    dbg "service_main left"
   end
 
   def service_stop
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop entered") }
+    dbg "service_stop entered"
     
-    File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop left") }
+    dbg "service_stop left"
   end
 end
 
diff --git a/lib/mongrel.rb b/lib/mongrel.rb
index dc928bc..8fccc90 100644
--- a/lib/mongrel.rb
+++ b/lib/mongrel.rb
@@ -4,8 +4,9 @@ require 'thread'
 require 'stringio'
 require 'mongrel/cgi'
 require 'mongrel/handlers'
+require 'mongrel/command'
 require 'mongrel/plugins'
-
+require 'timeout'
 
 # Mongrel module containing all of the classes (include C extensions) for running
 # a Mongrel web server.  It contains a minimalist HTTP server with just enough
@@ -31,6 +32,10 @@ module Mongrel
   class StopServer < Exception
   end
 
+  # Used to timeout worker threads that have taken too long
+  class TimeoutWorker < Exception
+  end
+
   # Every standard HTTP code mapped to the appropriate message.  These are
   # used so frequently that they are placed directly in Mongrel for easy
   # access rather than Mongrel::Const.
@@ -346,15 +351,18 @@ module Mongrel
       @host = host
       @port = port
       @processors = []
-      @timeout = timeout
 
-      num_processors.times {|i|
+      # create the worker threads
+      num_processors.times do |i|
         @processors << Thread.new do
           while client = @req_queue.deq
-            process_client(client)
+            Timeout::timeout(timeout) do
+              process_client(client)
+            end
           end
         end
-      }
+      end
+
     end
     
 
diff --git a/lib/mongrel/command.rb b/lib/mongrel/command.rb
index 3625438..04af386 100644
--- a/lib/mongrel/command.rb
+++ b/lib/mongrel/command.rb
@@ -1,7 +1,6 @@
 require 'singleton'
 require 'optparse'
-require 'pluginfactory'
-
+require 'mongrel/plugins'
 
 module Mongrel
 
@@ -10,17 +9,10 @@ module Mongrel
 
   module Command
 
-
     # A Command pattern implementation used to create the set of command available to the user
     # from Mongrel.  The script uses objects which implement this interface to do the
     # user's bidding.
-    #
-    # Creating a new command is very easy, and you can do it without modifying the source
-    # of Mongrel thanks to PluginFactory.  What you do is the following:
-    #
-    # 1.  
-    class Command
-      include PluginFactory
+    module Command
       
       attr_reader :valid, :done_validating
 
@@ -65,12 +57,6 @@ module Mongrel
         @opt.parse! argv
       end
       
-      # Tells the PluginFactory where to look for additional commands.  By default
-      # it's just a "plugins" directory wherever we are located.
-      def self.derivativeDirs
-        return ["plugins"]
-      end
-      
       # Returns true/false depending on whether the command is configured properly.
       def validate
         return @valid
@@ -119,8 +105,6 @@ module Mongrel
       end
     end
     
-    
-    
     # A Singleton class that manages all of the available commands
     # and handles running them.
     class Registry
@@ -128,15 +112,9 @@ module Mongrel
       
       # Builds a list of possible commands from the Command derivates list
       def commands
-        list = Command.derivatives()
-        match = Regexp.new("(.*::.*)|(.*command.*)", Regexp::IGNORECASE)
-        
-        results = []
-        list.keys.each do |key|
-          results << key.to_s unless match.match(key.to_s)
-        end
-        
-        return results.sort
+        pmgr = PluginManager.instance
+        list = pmgr.available["/commands"]
+        return list.sort
       end
 
       # Prints a list of available commands.
@@ -144,7 +122,7 @@ module Mongrel
         puts "Available commands are:\n\n"
         
         self.commands.each do |name|
-          puts " - #{name}\n"
+          puts " - #{name[1 .. -1]}\n"
         end
         
         puts "\nEach command takes -h as an option to get help."
@@ -165,8 +143,8 @@ module Mongrel
         
         # command exists, set it up and validate it
         begin
-          command = Command.create(cmd_name, args)
-        rescue FactoryError
+          command = PluginManager.instance.create("/commands/#{cmd_name}", args)
+        rescue
           STDERR.puts "INVALID COMMAND: #$!"
           print_command_list
           return
diff --git a/lib/mongrel/plugins.rb b/lib/mongrel/plugins.rb
index 9778e9b..f312e36 100644
--- a/lib/mongrel/plugins.rb
+++ b/lib/mongrel/plugins.rb
@@ -1,6 +1,47 @@
 require 'singleton'
 
 module Mongrel
+
+  # Implements the main method of managing plugins for Mongrel.
+  # "Plugins" in this sense are any classes which get registered
+  # with Mongrel for possible use when it's operating.  These can
+  # be Handlers, Commands, or other classes.  When you create a
+  # Plugin you register it into a URI-like namespace that makes
+  # it easy for you (and others) to reference it later during
+  # configuration.
+  #
+  # PluginManager is used as nothing more than a holder of all the
+  # plugins that have registered themselves.  Let's say you have:
+  #
+  #  class StopNow < Plugin "/commands"
+  #   ...
+  #  end
+  #
+  # Then you can get at this plugin with:
+  #
+  #  cmd = PluginManager.create("/commands/stopnow")
+  #
+  # The funky syntax for StopNow is a weird trick borrowed from
+  # the Camping framework. See the Mongrel::Plugin *function* (yes,
+  # function).  What this basically does is register it
+  # into the namespace for plugins at /commands.  You could go
+  # as arbitrarily nested as you like.
+  #
+  # Why this strange almost second namespace?  Why not just use
+  # the ObjectSpace and/or Modules?  The main reason is speed and
+  # to avoid cluttering the Ruby namespace with what is really a
+  # configuration statement.  This lets implementors put code
+  # into the Ruby structuring they need, and still have Plugins
+  # available to Mongrel via simple URI-like names.
+  #
+  # The alternative (as pluginfactory does it) is to troll through
+  # ObjectSpace looking for stuff that *might* be plugins every time
+  # one is needed.  This alternative also means that you are stuck
+  # naming your commands in specific ways and putting them in specific
+  # modules in order to configure how Mongrel should use them.
+  #
+  # One downside to this is that you need to subclass plugin to
+  # make it work.  In this case use mixins to add other functionality.
   class PluginManager
     include Singleton
     
@@ -8,6 +49,9 @@ module Mongrel
       @plugins = URIClassifier.new
     end
     
+    # Tell the PluginManager to scan the given path (recursively)
+    # and load the *.rb files found there.  This is how you'd
+    # setup your own plugin directory.
     def load(path)
       Dir.chdir(path) do
         Dir["**/*.rb"].each do |rbfile|
@@ -16,6 +60,9 @@ module Mongrel
       end
     end
     
+    # Not necessary for you to call directly, but this is
+    # how Mongrel::PluginBase.inherited actually adds a
+    # plugin to a category.
     def register(category, name, klass)
       cat, ignored, map = @plugins.resolve(category)
       if not cat
@@ -26,17 +73,21 @@ module Mongrel
       end
     end
     
-    
+    # Resolves the given name (should include /category/name) to
+    # find the plugin class and create an instance.  It uses
+    # the same URIClassifier that the rest of Mongrel does so it
+    # is fast.
     def create(name, options = {})
       category, plugin, map = @plugins.resolve(name)
       if category and plugin and plugin.length > 0
-        STDERR.puts "found: #{category} #{plugin} for #{name}"
         map[plugin].new(options)
       else
         raise "Plugin #{name} does not exist"
       end
     end
     
+    # Returns a map of URIs->[handlers] that you can
+    # use to investigate available handlers.
     def available
       map = {}
       @plugins.uris.each do |u|
@@ -50,19 +101,36 @@ module Mongrel
     
   end
 
+  # This base class for plugins reallys does nothing
+  # more than wire up the new class into the right category.
+  # It is not thread-safe yet but will be soon.
   class PluginBase
     
+    # See Mongrel::Plugin for an explanation.
     def PluginBase.inherited(klass)
-      
-      PluginManager.instance.register(@@category, klass.to_s.downcase, klass)
+      name = "/" + klass.to_s.downcase
+      PluginManager.instance.register(@@category, name, klass)
     end
     
+    # See Mongrel::Plugin for an explanation.
     def PluginBase.category=(category)
       @@category = category
     end
   end
   
-  def Plugin(c)
+  # This nifty function works with the PluginBase to give you
+  # the syntax:
+  #
+  #  class MyThing < Plugin "/things"
+  #    ...
+  #  end
+  #
+  # What it does is temporarily sets the PluginBase.category, and then
+  # returns PluginBase.  Since the next immediate thing Ruby does is
+  # use this returned class to create the new class, PluginBase.inherited
+  # gets called.  PluginBase.inherited then uses the set category, class name,
+  # and class to register the plugin in the right way.
+  def Mongrel::Plugin(c)
     PluginBase.category = c
     PluginBase
   end
diff --git a/lib/mongrel/rails.rb b/lib/mongrel/rails.rb
new file mode 100644
index 0000000..73d4272
--- /dev/null
+++ b/lib/mongrel/rails.rb
@@ -0,0 +1,68 @@
+require 'mongrel'
+
+
+# Implements a handler that can run Rails and serve files out of the
+# Rails application's public directory.  This lets you run your Rails
+# application with Mongrel during development and testing, then use it
+# also in production behind a server that's better at serving the
+# static files.
+#
+# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
+# mapping that it should add to the list of valid mime types.
+#
+# It also supports page caching directly and will try to resolve a request
+# in the following order:
+#
+# * If the requested exact PATH_INFO exists as a file then serve it.
+# * If it exists at PATH_INFO+".html" exists then serve that.
+# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
+#
+# This means that if you are using page caching it will actually work with Mongrel
+# and you should see a decent speed boost (but not as fast as if you use lighttpd).
+class RailsHandler < Mongrel::HttpHandler
+  def initialize(dir, mime_map = {})
+    @files = Mongrel::DirHandler.new(dir,false)
+    @guard = Mutex.new
+    
+    # register the requested mime types
+    mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
+  end
+  
+  # Attempts to resolve the request as follows:
+  #
+  #
+  # * If the requested exact PATH_INFO exists as a file then serve it.
+  # * If it exists at PATH_INFO+".html" exists then serve that.
+  # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
+  def process(request, response)
+    return if response.socket.closed?
+
+    path_info = request.params["PATH_INFO"]
+    page_cached = request.params["PATH_INFO"] + ".html"
+
+    if @files.can_serve(path_info)
+      # File exists as-is so serve it up
+      @files.process(request,response)
+    elsif @files.can_serve(page_cached)
+      # possible cached page, serve it up      
+      request.params["PATH_INFO"] = page_cached
+      @files.process(request,response)
+    else
+      cgi = Mongrel::CGIWrapper.new(request, response)
+
+      begin
+        @guard.synchronize do
+          # Rails is not thread safe so must be run entirely within synchronize
+          Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
+        end
+
+        # This finalizes the output using the proper HttpResponse way
+        cgi.out {""}
+      rescue Object => rails_error
+        STDERR.puts "calling Dispatcher.dispatch #{rails_error}"
+        STDERR.puts rails_error.backtrace.join("\n")
+      end
+    end
+  end
+
+end
diff --git a/lib/pluginfactory.rb b/lib/pluginfactory.rb
deleted file mode 100644
index 0a9a32f..0000000
--- a/lib/pluginfactory.rb
+++ /dev/null
@@ -1,384 +0,0 @@
-#!/usr/bin/env ruby -w
-#
-# This module contains the PluginFactory mixin. Including PluginFactory in your
-# class turns it into a factory for its derivatives, capable of searching for
-# and loading them by name. This is useful when you have an abstract base class
-# which defines an interface and basic functionality for a part of a larger
-# system, and a collection of subclasses which implement the interface for
-# different underlying functionality.
-#
-# An example of where this might be useful is in a program which talks to a
-# database. To avoid coupling it to a specific database, you use a Driver class
-# which encapsulates your program's interaction with the database behind a
-# useful interface. Now you can create a concrete implementation of the Driver
-# class for each kind of database you wish to talk to. If you make the base
-# Driver class a PluginFactory, too, you can add new drivers simply by dropping
-# them in a directory and using the Driver's <tt>create</tt> method to
-# instantiate them:
-#
-# == Creation Argument Variants
-#
-# The +create+ class method added to your class by PluginFactory searches for your module using
-#
-# == Synopsis
-#
-#---##### in driver.rb #####---
-#
-#        require "PluginFactory"
-#
-#        class Driver
-#                include PluginFactory
-#                def self::derivativeDirs
-#                   ["drivers"]
-#                end
-#        end
-#
-#---##########
-#
-#---##### in drivers/mysql.rb #####
-#
-#        require 'driver'
-#
-#        class MysqlDriver < Driver
-#                ...implementation...
-#        end
-#
-#---##########
-#
-#---##### in /usr/lib/ruby/1.8/PostgresDriver.rb #####
-#
-#        require 'driver'
-#
-#        class PostgresDriver < Driver
-#                ...implementation...
-#        end
-#
-#---##########
-#
-#---##### elsewhere #####
-#
-#        require 'driver'
-#
-#        config[:driver_type] #=> "mysql"
-#        driver = Driver::create( config[:driver_type] )
-#        driver.class #=> MysqlDriver
-#        pgdriver = Driver::create( "PostGresDriver" )
-#
-#---##########
-#
-# == Rcsid
-#
-# $Id: pluginfactory.rb 32 2005-03-05 00:21:05Z ged $
-#
-# == Authors
-#
-# * Martin Chase <stillflame@FaerieMUD.org>
-# * Michael Granger <ged@FaerieMUD.org>
-#
-# modified temporarily by Zed A. Shaw until license attribution can be given.
-#---
-#
-# Please see the file docs/COPYRIGHT for licensing details.
-#
-
-
-### An exception class for PluginFactory specific errors.
-class FactoryError < RuntimeError
-        def initialize( *args )
-                if ! args.empty?
-                        msg = args.collect {|a| a.to_s}.join
-                        super( msg )
-                else
-                        super( message )
-                end                                        
-        end
-end # class FactoryError
-
-
-### A mixin that adds PluginFactory class methods to a base class, so that
-### subclasses may be instantiated by name.
-module PluginFactory
-
-        ### A callback for logging the various debug and information this module
-        ### has to log.  Should take two arguments, the log level, possibly as a
-        ### symbol, and the log message itself.
-        @logger_callback = nil
-        class << self
-                attr_accessor :logger_callback
-        end
-
-        ### If the logger callback is set, use it to pass on a log entry.  First argument is
-        def self::log(level, *msg)
-                @logger_callback.call(level, msg.join) if @logger_callback
-        end
-
-
-        ### Inclusion callback -- extends the including class.
-        def self::included( klass )
-                klass.extend( self )
-        end
-
-
-        ### Raise an exception if the object being extended is anything but a
-        ### class.
-        def self::extend_object( obj )
-                unless obj.is_a?( Class )
-                        raise TypeError, "Cannot extend a #{obj.class.name}", caller(1)
-                end
-                obj.instance_variable_set( :@derivatives, {} )
-                super
-        end
-
-
-        #############################################################
-        ###        M I X I N   M E T H O D S
-        #############################################################
-
-        ### Return the Hash of derivative classes, keyed by various versions of
-        ### the class name.
-        def derivatives
-                ancestors.each {|klass|
-                        if klass.instance_variables.include?( "@derivatives" )
-                                break klass.instance_variable_get( :@derivatives )
-                        end
-                }
-        end
-
-
-        ### Returns the type name used when searching for a derivative.
-        def factoryType
-                base = nil
-                self.ancestors.each {|klass|
-                        if klass.instance_variables.include?( "@derivatives" )
-                                base = klass
-                                break
-                        end
-                }
-
-                raise FactoryError, "Couldn't find factory base for #{self.name}" if
-                        base.nil?
-
-                if base.name =~ /^.*::(.*)/
-                        return $1
-                else
-                        return base.name
-                end
-        end
-
-        
-        ### Inheritance callback -- Register subclasses in the derivatives hash
-        ### so that ::create knows about them.
-        def inherited( subclass )
-                keys = [ subclass.name, subclass.name.downcase, subclass ]
-
-                # Handle class names like 'FooBar' for 'Bar' factories.
-                if subclass.name.match( /(?:.*::)?(\w+)(?:#{self.factoryType})/i )
-                        keys << Regexp.last_match[1].downcase
-                else
-                        keys << subclass.name.sub( /.*::/, '' ).downcase
-                end
-
-                keys.uniq.each {|key|
-                        #PluginFactory::log :info, "Registering %s derivative of %s as %p" %
-                        #        [ subclass.name, self.name, key ]
-                        self.derivatives[ key ] = subclass
-                }
-                super
-        end
-
-
-        ### Returns an Array of registered derivatives
-        def derivativeClasses
-                self.derivatives.values.uniq
-        end
-
-
-        ### Given the <tt>className</tt> of the class to instantiate, and other
-        ### arguments bound for the constructor of the new object, this method
-        ### loads the derivative class if it is not loaded already (raising a
-        ### LoadError if an appropriately-named file cannot be found), and
-        ### instantiates it with the given <tt>args</tt>. The <tt>className</tt>
-        ### may be the the fully qualified name of the class, the class object
-        ### itself, or the unique part of the class name. The following examples
-        ### would all try to load and instantiate a class called "FooListener"
-        ### if Listener included Factory
-        ###   obj = Listener::create( 'FooListener' )
-        ###   obj = Listener::create( FooListener )
-        ###   obj = Listener::create( 'Foo' )
-        def create( subType, *args, &block )
-                subclass = getSubclass( subType )
-
-                return subclass.new( *args, &block )
-        rescue => err
-                nicetrace = err.backtrace.reject {|frame| /#{__FILE__}/ =~ frame}
-                msg = "When creating '#{subType}': " + err.message
-                Kernel::raise( err.class, msg, nicetrace )
-        end
-
-
-        ### Given a <tt>className</tt> like that of the first argument to
-        ### #create, attempt to load the corresponding class if it is not
-        ### already loaded and return the class object.
-        def getSubclass( className )
-                return self if ( self.name == className || className == '' )
-                return className if className.is_a?( Class ) && className >= self
-
-                unless self.derivatives.has_key?( className.downcase )
-                        self.loadDerivative( className )
-
-                        unless self.derivatives.has_key?( className.downcase )
-                                raise FactoryError,
-                                        "loadDerivative(%s) didn't add a '%s' key to the "\
-                                        "registry for %s" %
-                                        [ className, className.downcase, self.name ]
-                        end
-
-                        subclass = self.derivatives[ className.downcase ]
-                        unless subclass.is_a?( Class )
-                                raise FactoryError,
-                                        "loadDerivative(%s) added something other than a class "\
-                                        "to the registry for %s: %p" %
-                                        [ className, self.name, subclass ]
-                        end
-                end
-
-                return self.derivatives[ className.downcase ]
-        end
-
-
-        ### Calculates an appropriate filename for the derived class using the
-        ### name of the base class and tries to load it via <tt>require</tt>. If
-        ### the including class responds to a method named
-        ### <tt>derivativeDirs</tt>, its return value (either a String, or an
-        ### array of Strings) is added to the list of prefix directories to try
-        ### when attempting to require a modules. Eg., if
-        ### <tt>class.derivativeDirs</tt> returns <tt>['foo','bar']</tt> the
-        ### require line is tried with both <tt>'foo/'</tt> and <tt>'bar/'</tt>
-        ### prepended to it.
-        def loadDerivative( className )
-                className = className.to_s
-
-                #PluginFactory::log :debug, "Loading derivative #{className}"
-
-                # Get the unique part of the derived class name and try to
-                # load it from one of the derivative subdirs, if there are
-                # any.
-                modName = self.getModuleName( className )
-                self.requireDerivative( modName )
-
-                # Check to see if the specified listener is now loaded. If it
-                # is not, raise an error to that effect.
-                unless self.derivatives[ className.downcase ]
-                        raise FactoryError,
-                                "Couldn't find a %s named '%s'. Loaded derivatives are: %p" % [
-                                self.factoryType,
-                                className.downcase,
-                                self.derivatives.keys,
-                        ], caller(3)
-                end
-
-                return true
-        end
-
-
-        ### Build and return the unique part of the given <tt>className</tt>
-        ### either by stripping leading namespaces if the name already has the
-        ### name of the factory type in it (eg., 'My::FooService' for Service,
-        ### or by appending the factory type if it doesn't.
-        def getModuleName( className )
-                if className =~ /\w+#{self.factoryType}/
-                        modName = className.sub( /(?:.*::)?(\w+)(?:#{self.factoryType})/, "\\1" )
-                else
-                        modName = className
-                end
-
-                return modName
-        end
-
-
-        ### If the factory responds to the #derivativeDirs method, call
-        ### it and use the returned array as a list of directories to
-        ### search for the module with the specified <tt>modName</tt>.
-        def requireDerivative( modName )
-
-                # See if we have a list of special subdirs that derivatives
-                # live in
-                if ( self.respond_to?(:derivativeDirs) )
-                        subdirs = self.derivativeDirs
-                        subdirs = [ subdirs ] unless subdirs.is_a?( Array )
-
-                # If not, just try requiring it from $LOAD_PATH
-                else
-                        subdirs = ['']
-                end
-
-                fatals = []
-
-                # Iterate over the subdirs until we successfully require a
-                # module.
-                catch( :found ) {
-                        subdirs.collect {|dir| dir.strip}.each do |subdir|
-                                self.makeRequirePath( modName, subdir ).each {|path|
-                                        #PluginFactory::log :debug, "Trying #{path}..."
-
-                                        # Try to require the module, saving errors and jumping
-                                        # out of the catch block on success.
-                                        begin
-                                                require( path.untaint )
-                                        rescue LoadError => err
-                                                PluginFactory::log :debug,
-                                                        "No module at '%s', trying the next alternative: '%s'" %
-                                                        [ path, err.message ]
-                                        rescue ScriptError,StandardError => err
-                                                fatals << err
-                                                PluginFactory::log :error,
-                                                        "Found '#{path}', but encountered an error: %s\n\t%s" %
-                                                        [ err.message, err.backtrace.join("\n\t") ]
-                                        else
-                                                #PluginFactory::log :debug,
-                                                #        "Found '#{path}'. Throwing :found"
-                                                throw :found
-                                        end
-                                }
-                        end
-
-                        #PluginFactory::log :debug, "fatals = %p" % [ fatals ]
-
-                        # Re-raise is there was a file found, but it didn't load for
-                        # some reason.
-                        if ! fatals.empty?
-                                #PluginFactory::log :debug, "Re-raising first fatal error"
-                                Kernel::raise( fatals.first )
-                        end
-
-                        nil
-                }
-        end
-
-
-        ### Make a list of permutations of the given +modname+ for the given
-        ### +subdir+. Called on a +DataDriver+ class with the arguments 'Socket' and
-        ### 'drivers', returns:
-        ###   ["drivers/socketdatadriver", "drivers/socketDataDriver",
-        ###    "drivers/SocketDataDriver", "drivers/socket", "drivers/Socket"]
-        def makeRequirePath( modname, subdir )
-                path = []
-                myname = self.factoryType
-
-                # Make permutations of the two parts
-                path << modname
-                path << modname.downcase
-                path << modname                         + myname
-                path << modname.downcase + myname
-                path << modname.downcase + myname.downcase
-
-                # If a non-empty subdir was given, prepend it to all the items in the
-                # path
-                unless subdir.nil? or subdir.empty?
-                        path.collect! {|m| File::join(subdir, m)}
-                end
-
-                return path.uniq.reverse
-        end
-
-end # module Factory
diff --git a/test/plugins/commands/test1.rb b/test/plugins/commands/test1.rb
index b999d38..b8c0d89 100644
--- a/test/plugins/commands/test1.rb
+++ b/test/plugins/commands/test1.rb
@@ -1,19 +1,17 @@
 
-include Mongrel
-
-class First < Plugin "/commands"
+class First < Mongrel::Plugin "/commands"
   def initialize(options = {})
     puts "First with options: #{options.inspect}"
   end
 end
 
-class Second < Plugin "/commands"
+class Second < Mongrel::Plugin "/commands"
   def initialize(options = {})
     puts "Second with options: #{options.inspect}"
   end
 end
 
-class Last < Plugin "/commands"
+class Last < Mongrel::Plugin "/commands"
   def initialize(options = {})
     puts "Last with options: #{options.inspect}"
   end
diff --git a/test/test_plugins.rb b/test/test_plugins.rb
index 680dac5..0700e50 100644
--- a/test/test_plugins.rb
+++ b/test/test_plugins.rb
@@ -9,7 +9,7 @@ class PluginTest < Test::Unit::TestCase
   def setup
     @pmgr = PluginManager.instance
     @categories = ["/commands"]
-    @names = ["FirstCommand", "SecondCommand", "LastCommands"]
+    @names = ["/first","/second","/last"]
   end
 
   def test_load_plugins
@@ -17,13 +17,16 @@ class PluginTest < Test::Unit::TestCase
     puts "#{@pmgr.available.inspect}"
     @pmgr.available.each {|cat,plugins|
       plugins.each do |p|
-        puts "TEST: #{cat}/#{p}"
+        puts "TEST: #{cat}#{p}"
         assert @names.include?(p)
       end
     }
 
-    @pmgr.available.each do |name|
-      plugin = @pmgr.create(name, options={"name" => name})
+    @pmgr.available.each do |cat,plugins|
+      plugins.each do |p|
+        STDERR.puts "#{cat}#{p}"
+        plugin = @pmgr.create("#{cat}#{p}", options={"name" => p})
+      end
     end
   end