diff --git a/galaxy/util/pastescript/__init__.py b/galaxy/util/pastescript/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..45db1d62d60348053152613cc9267996e8681aef
--- /dev/null
+++ b/galaxy/util/pastescript/__init__.py
@@ -0,0 +1,3 @@
+"""
+Command for loading and serving wsgi apps taken from PasteScript
+"""
diff --git a/galaxy/util/pastescript/loadwsgi.py b/galaxy/util/pastescript/loadwsgi.py
new file mode 100644
index 0000000000000000000000000000000000000000..19ad1d3eea051db51042d623eab4c09d13c3ef64
--- /dev/null
+++ b/galaxy/util/pastescript/loadwsgi.py
@@ -0,0 +1,828 @@
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+# Mostly taken from PasteDeploy and stripped down for Galaxy
+
+from __future__ import with_statement
+
+import inspect
+import os
+import sys
+import re
+
+import pkg_resources
+
+__all__ = ['loadapp', 'loadserver', 'loadfilter', 'appconfig']
+
+# ---- from paste.deploy.compat --------------------------------------
+
+"""Python 2<->3 compatibility module"""
+
+def print_(template, *args, **kwargs):
+    template = str(template)
+    if args:
+        template = template % args
+    elif kwargs:
+        template = template % kwargs
+    sys.stdout.writelines(template)
+
+if sys.version_info < (3, 0):
+    basestring = basestring
+    from ConfigParser import ConfigParser
+    from urllib import unquote
+    iteritems = lambda d: d.iteritems()
+    dictkeys = lambda d: d.keys()
+
+    def reraise(t, e, tb):
+        exec('raise t, e, tb', dict(t=t, e=e, tb=tb))
+else:
+    basestring = str
+    from configparser import ConfigParser
+    from urllib.parse import unquote
+    iteritems = lambda d: d.items()
+    dictkeys = lambda d: list(d.keys())
+
+    def reraise(t, e, tb):
+        exec('raise e from tb', dict(e=e, tb=tb))
+
+# ---- from paste.deploy.util ----------------------------------------
+
+def fix_type_error(exc_info, callable, varargs, kwargs):
+    """
+    Given an exception, this will test if the exception was due to a
+    signature error, and annotate the error with better information if
+    so.
+
+    Usage::
+
+      try:
+          val = callable(*args, **kw)
+      except TypeError:
+          exc_info = fix_type_error(None, callable, args, kw)
+          raise exc_info[0], exc_info[1], exc_info[2]
+    """
+    if exc_info is None:
+        exc_info = sys.exc_info()
+    if (exc_info[0] != TypeError
+        or str(exc_info[1]).find('arguments') == -1
+        or getattr(exc_info[1], '_type_error_fixed', False)):
+        return exc_info
+    exc_info[1]._type_error_fixed = True
+    argspec = inspect.formatargspec(*inspect.getargspec(callable))
+    args = ', '.join(map(_short_repr, varargs))
+    if kwargs and args:
+        args += ', '
+    if kwargs:
+        kwargs = kwargs.items()
+        kwargs.sort()
+        args += ', '.join(['%s=...' % n for n, v in kwargs])
+    gotspec = '(%s)' % args
+    msg = '%s; got %s, wanted %s' % (exc_info[1], gotspec, argspec)
+    exc_info[1].args = (msg,)
+    return exc_info
+
+
+def _short_repr(v):
+    v = repr(v)
+    if len(v) > 12:
+        v = v[:8] + '...' + v[-4:]
+    return v
+
+
+def fix_call(callable, *args, **kw):
+    """
+    Call ``callable(*args, **kw)`` fixing any type errors that come out.
+    """
+    try:
+        val = callable(*args, **kw)
+    except TypeError:
+        exc_info = fix_type_error(None, callable, args, kw)
+        reraise(*exc_info)
+    return val
+
+
+def lookup_object(spec):
+    """
+    Looks up a module or object from a some.module:func_name specification.
+    To just look up a module, omit the colon and everything after it.
+    """
+    parts, target = spec.split(':') if ':' in spec else (spec, None)
+    module = __import__(parts)
+
+    for part in parts.split('.')[1:] + ([target] if target else []):
+        module = getattr(module, part)
+
+    return module
+
+# ---- from paste.deploy.loadwsgi ------------------------------------
+
+############################################################
+## Utility functions
+############################################################
+
+
+def import_string(s):
+    return pkg_resources.EntryPoint.parse("x=" + s).load(False)
+
+
+def _aslist(obj):
+    """
+    Turn object into a list; lists and tuples are left as-is, None
+    becomes [], and everything else turns into a one-element list.
+    """
+    if obj is None:
+        return []
+    elif isinstance(obj, (list, tuple)):
+        return obj
+    else:
+        return [obj]
+
+
+def _flatten(lst):
+    """
+    Flatten a nested list.
+    """
+    if not isinstance(lst, (list, tuple)):
+        return [lst]
+    result = []
+    for item in lst:
+        result.extend(_flatten(item))
+    return result
+
+
+class NicerConfigParser(ConfigParser):
+
+    def __init__(self, filename, *args, **kw):
+        ConfigParser.__init__(self, *args, **kw)
+        self.filename = filename
+        if hasattr(self, '_interpolation'):
+            self._interpolation = self.InterpolateWrapper(self._interpolation)
+
+    read_file = getattr(ConfigParser, 'read_file', ConfigParser.readfp)
+
+    def defaults(self):
+        """Return the defaults, with their values interpolated (with the
+        defaults dict itself)
+
+        Mainly to support defaults using values such as %(here)s
+        """
+        defaults = ConfigParser.defaults(self).copy()
+        for key, val in iteritems(defaults):
+            defaults[key] = self.get('DEFAULT', key) or val
+        return defaults
+
+    def _interpolate(self, section, option, rawval, vars):
+        # Python < 3.2
+        try:
+            return ConfigParser._interpolate(
+                self, section, option, rawval, vars)
+        except Exception:
+            e = sys.exc_info()[1]
+            args = list(e.args)
+            args[0] = 'Error in file %s: %s' % (self.filename, e)
+            e.args = tuple(args)
+            e.message = args[0]
+            raise
+
+    class InterpolateWrapper(object):
+        # Python >= 3.2
+        def __init__(self, original):
+            self._original = original
+
+        def __getattr__(self, name):
+            return getattr(self._original, name)
+
+        def before_get(self, parser, section, option, value, defaults):
+            try:
+                return self._original.before_get(parser, section, option,
+                                                 value, defaults)
+            except Exception:
+                e = sys.exc_info()[1]
+                args = list(e.args)
+                args[0] = 'Error in file %s: %s' % (parser.filename, e)
+                e.args = tuple(args)
+                e.message = args[0]
+                raise
+
+
+############################################################
+## Object types
+############################################################
+
+
+class _ObjectType(object):
+
+    name = None
+    egg_protocols = None
+    config_prefixes = None
+
+    def __init__(self):
+        # Normalize these variables:
+        self.egg_protocols = [_aslist(p) for p in _aslist(self.egg_protocols)]
+        self.config_prefixes = [_aslist(p) for p in _aslist(self.config_prefixes)]
+
+    def __repr__(self):
+        return '<%s protocols=%r prefixes=%r>' % (
+            self.name, self.egg_protocols, self.config_prefixes)
+
+    def invoke(self, context):
+        assert context.protocol in _flatten(self.egg_protocols)
+        return fix_call(context.object,
+                        context.global_conf, **context.local_conf)
+
+
+class _App(_ObjectType):
+
+    name = 'application'
+    egg_protocols = ['paste.app_factory', 'paste.composite_factory',
+                     'paste.composit_factory']
+    config_prefixes = [['app', 'application'], ['composite', 'composit'],
+                       'pipeline', 'filter-app']
+
+    def invoke(self, context):
+        if context.protocol in ('paste.composit_factory',
+                                'paste.composite_factory'):
+            return fix_call(context.object,
+                            context.loader, context.global_conf,
+                            **context.local_conf)
+        elif context.protocol == 'paste.app_factory':
+            return fix_call(context.object, context.global_conf, **context.local_conf)
+        else:
+            assert 0, "Protocol %r unknown" % context.protocol
+
+APP = _App()
+
+
+class _Filter(_ObjectType):
+    name = 'filter'
+    egg_protocols = [['paste.filter_factory', 'paste.filter_app_factory']]
+    config_prefixes = ['filter']
+
+    def invoke(self, context):
+        if context.protocol == 'paste.filter_factory':
+            return fix_call(context.object,
+                            context.global_conf, **context.local_conf)
+        elif context.protocol == 'paste.filter_app_factory':
+            def filter_wrapper(wsgi_app):
+                # This should be an object, so it has a nicer __repr__
+                return fix_call(context.object,
+                                wsgi_app, context.global_conf,
+                                **context.local_conf)
+            return filter_wrapper
+        else:
+            assert 0, "Protocol %r unknown" % context.protocol
+
+FILTER = _Filter()
+
+
+class _Server(_ObjectType):
+    name = 'server'
+    egg_protocols = [['paste.server_factory', 'paste.server_runner']]
+    config_prefixes = ['server']
+
+    def invoke(self, context):
+        if context.protocol == 'paste.server_factory':
+            return fix_call(context.object,
+                            context.global_conf, **context.local_conf)
+        elif context.protocol == 'paste.server_runner':
+            def server_wrapper(wsgi_app):
+                # This should be an object, so it has a nicer __repr__
+                return fix_call(context.object,
+                                wsgi_app, context.global_conf,
+                                **context.local_conf)
+            return server_wrapper
+        else:
+            assert 0, "Protocol %r unknown" % context.protocol
+
+SERVER = _Server()
+
+
+# Virtual type: (@@: There's clearly something crufty here;
+# this probably could be more elegant)
+class _PipeLine(_ObjectType):
+    name = 'pipeline'
+
+    def invoke(self, context):
+        app = context.app_context.create()
+        filters = [c.create() for c in context.filter_contexts]
+        filters.reverse()
+        for filter in filters:
+            app = filter(app)
+        return app
+
+PIPELINE = _PipeLine()
+
+
+class _FilterApp(_ObjectType):
+    name = 'filter_app'
+
+    def invoke(self, context):
+        next_app = context.next_context.create()
+        filter = context.filter_context.create()
+        return filter(next_app)
+
+FILTER_APP = _FilterApp()
+
+
+class _FilterWith(_App):
+    name = 'filtered_with'
+
+    def invoke(self, context):
+        filter = context.filter_context.create()
+        filtered = context.next_context.create()
+        if context.next_context.object_type is APP:
+            return filter(filtered)
+        else:
+            # filtering a filter
+            def composed(app):
+                return filter(filtered(app))
+            return composed
+
+FILTER_WITH = _FilterWith()
+
+
+############################################################
+## Loaders
+############################################################
+
+
+def loadapp(uri, name=None, **kw):
+    return loadobj(APP, uri, name=name, **kw)
+
+
+def loadfilter(uri, name=None, **kw):
+    return loadobj(FILTER, uri, name=name, **kw)
+
+
+def loadserver(uri, name=None, **kw):
+    return loadobj(SERVER, uri, name=name, **kw)
+
+
+def appconfig(uri, name=None, relative_to=None, global_conf=None):
+    context = loadcontext(APP, uri, name=name,
+                          relative_to=relative_to,
+                          global_conf=global_conf)
+    return context.config()
+
+_loaders = {}
+
+
+def loadobj(object_type, uri, name=None, relative_to=None,
+            global_conf=None):
+    context = loadcontext(
+        object_type, uri, name=name, relative_to=relative_to,
+        global_conf=global_conf)
+    return context.create()
+
+
+def loadcontext(object_type, uri, name=None, relative_to=None,
+                global_conf=None):
+    if '#' in uri:
+        if name is None:
+            uri, name = uri.split('#', 1)
+        else:
+            # @@: Ignore fragment or error?
+            uri = uri.split('#', 1)[0]
+    if name is None:
+        name = 'main'
+    if ':' not in uri:
+        raise LookupError("URI has no scheme: %r" % uri)
+    scheme, path = uri.split(':', 1)
+    scheme = scheme.lower()
+    if scheme not in _loaders:
+        raise LookupError(
+            "URI scheme not known: %r (from %s)"
+            % (scheme, ', '.join(_loaders.keys())))
+    return _loaders[scheme](
+        object_type,
+        uri, path, name=name, relative_to=relative_to,
+        global_conf=global_conf)
+
+
+def _loadconfig(object_type, uri, path, name, relative_to,
+                global_conf):
+    isabs = os.path.isabs(path)
+    # De-Windowsify the paths:
+    path = path.replace('\\', '/')
+    if not isabs:
+        if not relative_to:
+            raise ValueError(
+                "Cannot resolve relative uri %r; no relative_to keyword "
+                "argument given" % uri)
+        relative_to = relative_to.replace('\\', '/')
+        if relative_to.endswith('/'):
+            path = relative_to + path
+        else:
+            path = relative_to + '/' + path
+    if path.startswith('///'):
+        path = path[2:]
+    path = unquote(path)
+    loader = ConfigLoader(path)
+    if global_conf:
+        loader.update_defaults(global_conf, overwrite=False)
+    return loader.get_context(object_type, name, global_conf)
+
+_loaders['config'] = _loadconfig
+
+
+def _loadegg(object_type, uri, spec, name, relative_to,
+             global_conf):
+    loader = EggLoader(spec)
+    return loader.get_context(object_type, name, global_conf)
+
+_loaders['egg'] = _loadegg
+
+
+def _loadfunc(object_type, uri, spec, name, relative_to,
+             global_conf):
+
+    loader = FuncLoader(spec)
+    return loader.get_context(object_type, name, global_conf)
+
+_loaders['call'] = _loadfunc
+
+############################################################
+## Loaders
+############################################################
+
+
+class _Loader(object):
+
+    def get_app(self, name=None, global_conf=None):
+        return self.app_context(
+            name=name, global_conf=global_conf).create()
+
+    def get_filter(self, name=None, global_conf=None):
+        return self.filter_context(
+            name=name, global_conf=global_conf).create()
+
+    def get_server(self, name=None, global_conf=None):
+        return self.server_context(
+            name=name, global_conf=global_conf).create()
+
+    def app_context(self, name=None, global_conf=None):
+        return self.get_context(
+            APP, name=name, global_conf=global_conf)
+
+    def filter_context(self, name=None, global_conf=None):
+        return self.get_context(
+            FILTER, name=name, global_conf=global_conf)
+
+    def server_context(self, name=None, global_conf=None):
+        return self.get_context(
+            SERVER, name=name, global_conf=global_conf)
+
+    _absolute_re = re.compile(r'^[a-zA-Z]+:')
+
+    def absolute_name(self, name):
+        """
+        Returns true if the name includes a scheme
+        """
+        if name is None:
+            return False
+        return self._absolute_re.search(name)
+
+
+class ConfigLoader(_Loader):
+
+    def __init__(self, filename):
+        self.filename = filename = filename.strip()
+        defaults = {
+            'here': os.path.dirname(os.path.abspath(filename)),
+            '__file__': os.path.abspath(filename)
+            }
+        self.parser = NicerConfigParser(filename, defaults=defaults)
+        self.parser.optionxform = str  # Don't lower-case keys
+        with open(filename) as f:
+            self.parser.read_file(f)
+
+    def update_defaults(self, new_defaults, overwrite=True):
+        for key, value in iteritems(new_defaults):
+            if not overwrite and key in self.parser._defaults:
+                continue
+            self.parser._defaults[key] = value
+
+    def get_context(self, object_type, name=None, global_conf=None):
+        if self.absolute_name(name):
+            return loadcontext(object_type, name,
+                               relative_to=os.path.dirname(self.filename),
+                               global_conf=global_conf)
+        section = self.find_config_section(
+            object_type, name=name)
+        if global_conf is None:
+            global_conf = {}
+        else:
+            global_conf = global_conf.copy()
+        defaults = self.parser.defaults()
+        global_conf.update(defaults)
+        local_conf = {}
+        global_additions = {}
+        get_from_globals = {}
+        for option in self.parser.options(section):
+            if option.startswith('set '):
+                name = option[4:].strip()
+                global_additions[name] = global_conf[name] = (
+                    self.parser.get(section, option))
+            elif option.startswith('get '):
+                name = option[4:].strip()
+                get_from_globals[name] = self.parser.get(section, option)
+            else:
+                if option in defaults:
+                    # @@: It's a global option (?), so skip it
+                    continue
+                local_conf[option] = self.parser.get(section, option)
+        for local_var, glob_var in get_from_globals.items():
+            local_conf[local_var] = global_conf[glob_var]
+        if object_type in (APP, FILTER) and 'filter-with' in local_conf:
+            filter_with = local_conf.pop('filter-with')
+        else:
+            filter_with = None
+        if 'require' in local_conf:
+            for spec in local_conf['require'].split():
+                pkg_resources.require(spec)
+            del local_conf['require']
+        if section.startswith('filter-app:'):
+            context = self._filter_app_context(
+                object_type, section, name=name,
+                global_conf=global_conf, local_conf=local_conf,
+                global_additions=global_additions)
+        elif section.startswith('pipeline:'):
+            context = self._pipeline_app_context(
+                object_type, section, name=name,
+                global_conf=global_conf, local_conf=local_conf,
+                global_additions=global_additions)
+        elif 'use' in local_conf:
+            context = self._context_from_use(
+                object_type, local_conf, global_conf, global_additions,
+                section)
+        else:
+            context = self._context_from_explicit(
+                object_type, local_conf, global_conf, global_additions,
+                section)
+        if filter_with is not None:
+            filter_with_context = LoaderContext(
+                obj=None,
+                object_type=FILTER_WITH,
+                protocol=None,
+                global_conf=global_conf, local_conf=local_conf,
+                loader=self)
+            filter_with_context.filter_context = self.filter_context(
+                name=filter_with, global_conf=global_conf)
+            filter_with_context.next_context = context
+            return filter_with_context
+        return context
+
+    def _context_from_use(self, object_type, local_conf, global_conf,
+                          global_additions, section):
+        use = local_conf.pop('use')
+        context = self.get_context(
+            object_type, name=use, global_conf=global_conf)
+        context.global_conf.update(global_additions)
+        context.local_conf.update(local_conf)
+        if '__file__' in global_conf:
+            # use sections shouldn't overwrite the original __file__
+            context.global_conf['__file__'] = global_conf['__file__']
+        # @@: Should loader be overwritten?
+        context.loader = self
+
+        if context.protocol is None:
+            # Determine protocol from section type
+            section_protocol = section.split(':', 1)[0]
+            if section_protocol in ('application', 'app'):
+                context.protocol = 'paste.app_factory'
+            elif section_protocol in ('composit', 'composite'):
+                context.protocol = 'paste.composit_factory'
+            else:
+                # This will work with 'server' and 'filter', otherwise it
+                # could fail but there is an error message already for
+                # bad protocols
+                context.protocol = 'paste.%s_factory' % section_protocol
+
+        return context
+
+    def _context_from_explicit(self, object_type, local_conf, global_conf,
+                               global_addition, section):
+        possible = []
+        for protocol_options in object_type.egg_protocols:
+            for protocol in protocol_options:
+                if protocol in local_conf:
+                    possible.append((protocol, local_conf[protocol]))
+                    break
+        if len(possible) > 1:
+            raise LookupError(
+                "Multiple protocols given in section %r: %s"
+                % (section, possible))
+        if not possible:
+            raise LookupError(
+                "No loader given in section %r" % section)
+        found_protocol, found_expr = possible[0]
+        del local_conf[found_protocol]
+        value = import_string(found_expr)
+        context = LoaderContext(
+            value, object_type, found_protocol,
+            global_conf, local_conf, self)
+        return context
+
+    def _filter_app_context(self, object_type, section, name,
+                            global_conf, local_conf, global_additions):
+        if 'next' not in local_conf:
+            raise LookupError(
+                "The [%s] section in %s is missing a 'next' setting"
+                % (section, self.filename))
+        next_name = local_conf.pop('next')
+        context = LoaderContext(None, FILTER_APP, None, global_conf,
+                                local_conf, self)
+        context.next_context = self.get_context(
+            APP, next_name, global_conf)
+        if 'use' in local_conf:
+            context.filter_context = self._context_from_use(
+                FILTER, local_conf, global_conf, global_additions,
+                section)
+        else:
+            context.filter_context = self._context_from_explicit(
+                FILTER, local_conf, global_conf, global_additions,
+                section)
+        return context
+
+    def _pipeline_app_context(self, object_type, section, name,
+                              global_conf, local_conf, global_additions):
+        if 'pipeline' not in local_conf:
+            raise LookupError(
+                "The [%s] section in %s is missing a 'pipeline' setting"
+                % (section, self.filename))
+        pipeline = local_conf.pop('pipeline').split()
+        if local_conf:
+            raise LookupError(
+                "The [%s] pipeline section in %s has extra "
+                "(disallowed) settings: %s"
+                % (', '.join(local_conf.keys())))
+        context = LoaderContext(None, PIPELINE, None, global_conf,
+                                local_conf, self)
+        context.app_context = self.get_context(
+            APP, pipeline[-1], global_conf)
+        context.filter_contexts = [
+            self.get_context(FILTER, name, global_conf)
+            for name in pipeline[:-1]]
+        return context
+
+    def find_config_section(self, object_type, name=None):
+        """
+        Return the section name with the given name prefix (following the
+        same pattern as ``protocol_desc`` in ``config``.  It must have the
+        given name, or for ``'main'`` an empty name is allowed.  The
+        prefix must be followed by a ``:``.
+
+        Case is *not* ignored.
+        """
+        possible = []
+        for name_options in object_type.config_prefixes:
+            for name_prefix in name_options:
+                found = self._find_sections(
+                    self.parser.sections(), name_prefix, name)
+                if found:
+                    possible.extend(found)
+                    break
+        if not possible:
+            raise LookupError(
+                "No section %r (prefixed by %s) found in config %s"
+                % (name,
+                   ' or '.join(map(repr, _flatten(object_type.config_prefixes))),
+                   self.filename))
+        if len(possible) > 1:
+            raise LookupError(
+                "Ambiguous section names %r for section %r (prefixed by %s) "
+                "found in config %s"
+                % (possible, name,
+                   ' or '.join(map(repr, _flatten(object_type.config_prefixes))),
+                   self.filename))
+        return possible[0]
+
+    def _find_sections(self, sections, name_prefix, name):
+        found = []
+        if name is None:
+            if name_prefix in sections:
+                found.append(name_prefix)
+            name = 'main'
+        for section in sections:
+            if section.startswith(name_prefix + ':'):
+                if section[len(name_prefix) + 1:].strip() == name:
+                    found.append(section)
+        return found
+
+
+class EggLoader(_Loader):
+
+    def __init__(self, spec):
+        self.spec = spec
+
+    def get_context(self, object_type, name=None, global_conf=None):
+        if self.absolute_name(name):
+            return loadcontext(object_type, name,
+                               global_conf=global_conf)
+        entry_point, protocol, ep_name = self.find_egg_entry_point(
+            object_type, name=name)
+        return LoaderContext(
+            entry_point,
+            object_type,
+            protocol,
+            global_conf or {}, {},
+            self,
+            distribution=pkg_resources.get_distribution(self.spec),
+            entry_point_name=ep_name)
+
+    def find_egg_entry_point(self, object_type, name=None):
+        """
+        Returns the (entry_point, protocol) for the with the given
+        ``name``.
+        """
+        if name is None:
+            name = 'main'
+        possible = []
+        for protocol_options in object_type.egg_protocols:
+            for protocol in protocol_options:
+                pkg_resources.require(self.spec)
+                entry = pkg_resources.get_entry_info(
+                    self.spec,
+                    protocol,
+                    name)
+                if entry is not None:
+                    possible.append((entry.load(), protocol, entry.name))
+                    break
+        if not possible:
+            # Better exception
+            dist = pkg_resources.get_distribution(self.spec)
+            raise LookupError(
+                "Entry point %r not found in egg %r (dir: %s; protocols: %s; "
+                "entry_points: %s)"
+                % (name, self.spec,
+                   dist.location,
+                   ', '.join(_flatten(object_type.egg_protocols)),
+                   ', '.join(_flatten([
+                dictkeys(pkg_resources.get_entry_info(self.spec, prot, name) or {})
+                for prot in protocol_options] or '(no entry points)'))))
+        if len(possible) > 1:
+            raise LookupError(
+                "Ambiguous entry points for %r in egg %r (protocols: %s)"
+                % (name, self.spec, ', '.join(_flatten(protocol_options))))
+        return possible[0]
+
+
+class FuncLoader(_Loader):
+    """ Loader that supports specifying functions inside modules, without
+    using eggs at all. Configuration should be in the format:
+        use = call:my.module.path:function_name
+
+    Dot notation is supported in both the module and function name, e.g.:
+        use = call:my.module.path:object.method
+    """
+    def __init__(self, spec):
+        self.spec = spec
+        if not ':' in spec:
+            raise LookupError("Configuration not in format module:function")
+
+    def get_context(self, object_type, name=None, global_conf=None):
+        obj = lookup_object(self.spec)
+        return LoaderContext(
+            obj,
+            object_type,
+            None, # determine protocol from section type
+            global_conf or {},
+            {},
+            self,
+            )
+
+
+class LoaderContext(object):
+
+    def __init__(self, obj, object_type, protocol,
+                 global_conf, local_conf, loader,
+                 distribution=None, entry_point_name=None):
+        self.object = obj
+        self.object_type = object_type
+        self.protocol = protocol
+        #assert protocol in _flatten(object_type.egg_protocols), (
+        #    "Bad protocol %r; should be one of %s"
+        #    % (protocol, ', '.join(map(repr, _flatten(object_type.egg_protocols)))))
+        self.global_conf = global_conf
+        self.local_conf = local_conf
+        self.loader = loader
+        self.distribution = distribution
+        self.entry_point_name = entry_point_name
+
+    def create(self):
+        return self.object_type.invoke(self)
+
+    def config(self):
+        conf = AttrDict(self.global_conf)
+        conf.update(self.local_conf)
+        conf.local_conf = self.local_conf
+        conf.global_conf = self.global_conf
+        conf.context = self
+        return conf
+
+
+class AttrDict(dict):
+    """
+    A dictionary that can be assigned to.
+    """
+    pass
diff --git a/galaxy/util/pastescript/serve.py b/galaxy/util/pastescript/serve.py
new file mode 100644
index 0000000000000000000000000000000000000000..4bd3a0c9b4eb36eb6b88fd5f9b0d578a898f2cb6
--- /dev/null
+++ b/galaxy/util/pastescript/serve.py
@@ -0,0 +1,1059 @@
+# Most of this code is:
+
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+# The server command includes the additional header:
+
+# For discussion of daemonizing:
+#   http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/278731
+# Code taken also from QP:
+#   http://www.mems-exchange.org/software/qp/
+#   From lib/site.py
+
+# Galaxy originally used PasteScript and PasteDeploy for application
+# loading, to maintain compatibility we've internalized some of that
+# code here, stripping out uneeded functionality.
+
+# All top level imports from each package moved here and organized
+import ConfigParser
+import atexit
+import errno
+import getpass
+import logging
+import optparse
+import os
+import re
+import subprocess
+import sys
+import textwrap
+import threading
+import time
+from logging.config import fileConfig
+
+from loadwsgi import loadapp, loadserver
+
+
+difflib = None
+
+# ---- from paste.script.bool_optparse --------------------------------
+
+"""
+A subclass of ``optparse.OptionParser`` that allows boolean long
+options (like ``--verbose``) to also take arguments (like
+``--verbose=true``).  Arguments *must* use ``=``.
+"""
+
+try:
+    _ = optparse._
+except AttributeError:
+    from gettext import gettext as _
+
+class BoolOptionParser(optparse.OptionParser):
+
+    def _process_long_opt(self, rargs, values):
+        arg = rargs.pop(0)
+
+        # Value explicitly attached to arg?  Pretend it's the next
+        # argument.
+        if "=" in arg:
+            (opt, next_arg) = arg.split("=", 1)
+            rargs.insert(0, next_arg)
+            had_explicit_value = True
+        else:
+            opt = arg
+            had_explicit_value = False
+
+        opt = self._match_long_opt(opt)
+        option = self._long_opt[opt]
+        if option.takes_value():
+            nargs = option.nargs
+            if len(rargs) < nargs:
+                if nargs == 1:
+                    self.error(_("%s option requires an argument") % opt)
+                else:
+                    self.error(_("%s option requires %d arguments")
+                               % (opt, nargs))
+            elif nargs == 1:
+                value = rargs.pop(0)
+            else:
+                value = tuple(rargs[0:nargs])
+                del rargs[0:nargs]
+
+        elif had_explicit_value:
+            value = rargs[0].lower().strip()
+            del rargs[0:1]
+            if value in ('true', 'yes', 'on', '1', 'y', 't'):
+                value = None
+            elif value in ('false', 'no', 'off', '0', 'n', 'f'):
+                # Don't process
+                return
+            else:
+                self.error(_('%s option takes a boolean value only (true/false)') % opt)
+
+        else:
+            value = None
+
+        option.process(opt, value, values, self)
+
+# ---- from paste.script.command --------------------------------------
+
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+
+class BadCommand(Exception):
+
+    def __init__(self, message, exit_code=2):
+        self.message = message
+        self.exit_code = exit_code
+        Exception.__init__(self, message)
+
+    def _get_message(self):
+        """Getter for 'message'; needed only to override deprecation
+        in BaseException."""
+        return self.__message
+
+    def _set_message(self, value):
+        """Setter for 'message'; needed only to override deprecation
+        in BaseException."""
+        self.__message = value
+
+    # BaseException.message has been deprecated since Python 2.6.
+    # To prevent DeprecationWarning from popping up over this
+    # pre-existing attribute, use a new property that takes lookup
+    # precedence.
+    message = property(_get_message, _set_message)
+
+class NoDefault(object):
+    pass
+
+# run and invoke methods moved below ServeCommand
+
+class Command(object):
+
+    def __init__(self, name):
+        self.command_name = name
+
+    max_args = None
+    max_args_error = 'You must provide no more than %(max_args)s arguments'
+    min_args = None
+    min_args_error = 'You must provide at least %(min_args)s arguments'
+    required_args = None
+    # If this command takes a configuration file, set this to 1 or -1
+    # Then if invoked through #! the config file will be put into the positional
+    # arguments -- at the beginning with 1, at the end with -1
+    takes_config_file = None
+
+    # Grouped in help messages by this:
+    group_name = ''
+
+    required_args = ()
+    description = None
+    usage = ''
+    hidden = False
+    # This is the default verbosity level; --quiet subtracts,
+    # --verbose adds:
+    default_verbosity = 0
+    # This is the default interactive state:
+    default_interactive = 0
+    return_code = 0
+
+    BadCommand = BadCommand
+
+    # Must define:
+    #   parser
+    #   summary
+    #   command()
+
+    def run(self, args):
+        self.parse_args(args)
+
+        # Setup defaults:
+        for name, default in [('verbose', 0),
+                              ('quiet', 0),
+                              ('interactive', False),
+                              ('overwrite', False)]:
+            if not hasattr(self.options, name):
+                setattr(self.options, name, default)
+        if getattr(self.options, 'simulate', False):
+            self.options.verbose = max(self.options.verbose, 1)
+        self.interactive = self.default_interactive
+        if getattr(self.options, 'interactive', False):
+            self.interactive += self.options.interactive
+        if getattr(self.options, 'no_interactive', False):
+            self.interactive = False
+        self.verbose = self.default_verbosity
+        self.verbose += self.options.verbose
+        self.verbose -= self.options.quiet
+        self.simulate = getattr(self.options, 'simulate', False)
+
+        # For #! situations:
+        if (os.environ.get('PASTE_CONFIG_FILE')
+            and self.takes_config_file is not None):
+            take = self.takes_config_file
+            filename = os.environ.get('PASTE_CONFIG_FILE')
+            if take == 1:
+                self.args.insert(0, filename)
+            elif take == -1:
+                self.args.append(filename)
+            else:
+                assert 0, (
+                    "Value takes_config_file must be None, 1, or -1 (not %r)"
+                    % take)
+
+        if (os.environ.get('PASTE_DEFAULT_QUIET')):
+            self.verbose = 0
+
+        # Validate:
+        if self.min_args is not None and len(self.args) < self.min_args:
+            raise BadCommand(
+                self.min_args_error % {'min_args': self.min_args,
+                                       'actual_args': len(self.args)})
+        if self.max_args is not None and len(self.args) > self.max_args:
+            raise BadCommand(
+                self.max_args_error % {'max_args': self.max_args,
+                                       'actual_args': len(self.args)})
+        for var_name, option_name in self.required_args:
+            if not getattr(self.options, var_name, None):
+                raise BadCommand(
+                    'You must provide the option %s' % option_name)
+        result = self.command()
+        if result is None:
+            return self.return_code
+        else:
+            return result
+
+    def parse_args(self, args):
+        if self.usage:
+            usage = ' '+self.usage
+        else:
+            usage = ''
+        self.parser.usage = "%%prog [options]%s\n%s" % (
+            usage, self.summary)
+        self.parser.prog = self._prog_name()
+        if self.description:
+            desc = self.description
+            desc = textwrap.dedent(desc)
+            self.parser.description = desc
+        self.options, self.args = self.parser.parse_args(args)
+
+    def _prog_name(self):
+        return '%s %s' % (os.path.basename(sys.argv[0]), self.command_name)
+
+    ########################################
+    ## Utility methods
+    ########################################
+
+    def pad(self, s, length, dir='left'):
+        if len(s) >= length:
+            return s
+        if dir == 'left':
+            return s + ' '*(length-len(s))
+        else:
+            return ' '*(length-len(s)) + s
+
+    def standard_parser(cls, verbose=True,
+                        interactive=False,
+                        no_interactive=False,
+                        simulate=False,
+                        quiet=False,
+                        overwrite=False):
+        """
+        Create a standard ``OptionParser`` instance.
+
+        Typically used like::
+
+            class MyCommand(Command):
+                parser = Command.standard_parser()
+
+        Subclasses may redefine ``standard_parser``, so use the
+        nearest superclass's class method.
+        """
+        parser = BoolOptionParser()
+        if verbose:
+            parser.add_option('-v', '--verbose',
+                              action='count',
+                              dest='verbose',
+                              default=0)
+        if quiet:
+            parser.add_option('-q', '--quiet',
+                              action='count',
+                              dest='quiet',
+                              default=0)
+        if no_interactive:
+            parser.add_option('--no-interactive',
+                              action="count",
+                              dest="no_interactive",
+                              default=0)
+        if interactive:
+            parser.add_option('-i', '--interactive',
+                              action='count',
+                              dest='interactive',
+                              default=0)
+        if simulate:
+            parser.add_option('-n', '--simulate',
+                              action='store_true',
+                              dest='simulate',
+                              default=False)
+        if overwrite:
+            parser.add_option('-f', '--overwrite',
+                              dest="overwrite",
+                              action="store_true",
+                              help="Overwrite files (warnings will be emitted for non-matching files otherwise)")
+        return parser
+
+    standard_parser = classmethod(standard_parser)
+
+    def quote_first_command_arg(self, arg):
+        """
+        There's a bug in Windows when running an executable that's
+        located inside a path with a space in it.  This method handles
+        that case, or on non-Windows systems or an executable with no
+        spaces, it just leaves well enough alone.
+        """
+        if (sys.platform != 'win32'
+            or ' ' not in arg):
+            # Problem does not apply:
+            return arg
+        try:
+            import win32api
+        except ImportError:
+            raise ValueError(
+                "The executable %r contains a space, and in order to "
+                "handle this issue you must have the win32api module "
+                "installed" % arg)
+        arg = win32api.GetShortPathName(arg)
+        return arg
+
+    def parse_vars(self, args):
+        """
+        Given variables like ``['a=b', 'c=d']`` turns it into ``{'a':
+        'b', 'c': 'd'}``
+        """
+        result = {}
+        for arg in args:
+            if '=' not in arg:
+                raise BadCommand(
+                    'Variable assignment %r invalid (no "=")'
+                    % arg)
+            name, value = arg.split('=', 1)
+            result[name] = value
+        return result
+
+
+    def logging_file_config(self, config_file):
+        """
+        Setup logging via the logging module's fileConfig function with the
+        specified ``config_file``, if applicable.
+
+        ConfigParser defaults are specified for the special ``__file__``
+        and ``here`` variables, similar to PasteDeploy config loading.
+        """
+        parser = ConfigParser.ConfigParser()
+        parser.read([config_file])
+        if parser.has_section('loggers'):
+            config_file = os.path.abspath(config_file)
+            fileConfig(config_file, dict(__file__=config_file,
+                                         here=os.path.dirname(config_file)))
+
+class NotFoundCommand(Command):
+
+    def run(self, args):
+        #for name, value in os.environ.items():
+        #    print '%s: %s' % (name, value)
+        #print sys.argv
+        print ('Command %r not known (you may need to run setup.py egg_info)'
+               % self.command_name)
+        commands = get_commands().items()
+        commands.sort()
+        if not commands:
+            print 'No commands registered.'
+            print 'Have you installed Paste Script?'
+            print '(try running python setup.py develop)'
+            return 2
+        print 'Known commands:'
+        longest = max([len(n) for n, c in commands])
+        for name, command in commands:
+            print '  %s  %s' % (self.pad(name, length=longest),
+                                command.load().summary)
+        return 2
+
+
+# ---- From paste.script.serve ----------------------------------------
+
+MAXFD = 1024
+
+jython = sys.platform.startswith('java')
+
+class DaemonizeException(Exception):
+    pass
+
+
+class ServeCommand(Command):
+
+    min_args = 0
+    usage = 'CONFIG_FILE [start|stop|restart|status] [var=value]'
+    takes_config_file = 1
+    summary = "Serve the described application"
+    description = """\
+    This command serves a web application that uses a paste.deploy
+    configuration file for the server and application.
+
+    If start/stop/restart is given, then --daemon is implied, and it will
+    start (normal operation), stop (--stop-daemon), or do both.
+
+    You can also include variable assignments like 'http_port=8080'
+    and then use %(http_port)s in your config files.
+    """
+
+    # used by subclasses that configure apps and servers differently
+    requires_config_file = True
+
+    parser = Command.standard_parser(quiet=True)
+    parser.add_option('-n', '--app-name',
+                      dest='app_name',
+                      metavar='NAME',
+                      help="Load the named application (default main)")
+    parser.add_option('-s', '--server',
+                      dest='server',
+                      metavar='SERVER_TYPE',
+                      help="Use the named server.")
+    parser.add_option('--server-name',
+                      dest='server_name',
+                      metavar='SECTION_NAME',
+                      help="Use the named server as defined in the configuration file (default: main)")
+    if hasattr(os, 'fork'):
+        parser.add_option('--daemon',
+                          dest="daemon",
+                          action="store_true",
+                          help="Run in daemon (background) mode")
+    parser.add_option('--pid-file',
+                      dest='pid_file',
+                      metavar='FILENAME',
+                      help="Save PID to file (default to paster.pid if running in daemon mode)")
+    parser.add_option('--log-file',
+                      dest='log_file',
+                      metavar='LOG_FILE',
+                      help="Save output to the given log file (redirects stdout)")
+    parser.add_option('--reload',
+                      dest='reload',
+                      action='store_true',
+                      help="Use auto-restart file monitor")
+    parser.add_option('--reload-interval',
+                      dest='reload_interval',
+                      default=1,
+                      help="Seconds between checking files (low number can cause significant CPU usage)")
+    parser.add_option('--monitor-restart',
+                      dest='monitor_restart',
+                      action='store_true',
+                      help="Auto-restart server if it dies")
+    parser.add_option('--status',
+                      action='store_true',
+                      dest='show_status',
+                      help="Show the status of the (presumably daemonized) server")
+
+
+    if hasattr(os, 'setuid'):
+        # I don't think these are available on Windows
+        parser.add_option('--user',
+                          dest='set_user',
+                          metavar="USERNAME",
+                          help="Set the user (usually only possible when run as root)")
+        parser.add_option('--group',
+                          dest='set_group',
+                          metavar="GROUP",
+                          help="Set the group (usually only possible when run as root)")
+
+    parser.add_option('--stop-daemon',
+                      dest='stop_daemon',
+                      action='store_true',
+                      help='Stop a daemonized server (given a PID file, or default paster.pid file)')
+
+    if jython:
+        parser.add_option('--disable-jython-reloader',
+                          action='store_true',
+                          dest='disable_jython_reloader',
+                          help="Disable the Jython reloader")
+
+
+    _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I)
+
+    default_verbosity = 1
+
+    _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN'
+    _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN'
+
+    possible_subcommands = ('start', 'stop', 'restart', 'status')
+
+    def command(self):
+        if self.options.stop_daemon:
+            return self.stop_daemon()
+
+        if not hasattr(self.options, 'set_user'):
+            # Windows case:
+            self.options.set_user = self.options.set_group = None
+        # @@: Is this the right stage to set the user at?
+        self.change_user_group(
+            self.options.set_user, self.options.set_group)
+
+        if self.requires_config_file:
+            if not self.args:
+                raise BadCommand('You must give a config file')
+            app_spec = self.args[0]
+            if (len(self.args) > 1
+                and self.args[1] in self.possible_subcommands):
+                cmd = self.args[1]
+                restvars = self.args[2:]
+            else:
+                cmd = None
+                restvars = self.args[1:]
+        else:
+            app_spec = ""
+            if (self.args
+                and self.args[0] in self.possible_subcommands):
+                cmd = self.args[0]
+                restvars = self.args[1:]
+            else:
+                cmd = None
+                restvars = self.args[:]
+
+        if (getattr(self.options, 'daemon', False)
+            and getattr(self.options, 'reload', False)):
+            raise BadCommand('The --daemon and --reload options may not be used together')
+
+        jython_monitor = False
+        if self.options.reload:
+            if jython and not self.options.disable_jython_reloader:
+                # JythonMonitor raises the special SystemRestart
+                # exception that'll cause the Jython interpreter to
+                # reload in the existing Java process (avoiding
+                # subprocess startup time)
+                try:
+                    from paste.reloader import JythonMonitor
+                except ImportError:
+                    pass
+                else:
+                    jython_monitor = JythonMonitor(poll_interval=int(
+                            self.options.reload_interval))
+                    if self.requires_config_file:
+                        jython_monitor.watch_file(self.args[0])
+
+            if not jython_monitor:
+                if os.environ.get(self._reloader_environ_key):
+                    from paste import reloader
+                    if self.verbose > 1:
+                        print 'Running reloading file monitor'
+                    reloader.install(int(self.options.reload_interval))
+                    if self.requires_config_file:
+                        reloader.watch_file(self.args[0])
+                else:
+                    return self.restart_with_reloader()
+
+        if cmd not in (None, 'start', 'stop', 'restart', 'status'):
+            raise BadCommand(
+                'Error: must give start|stop|restart (not %s)' % cmd)
+
+        if cmd == 'status' or self.options.show_status:
+            return self.show_status()
+
+        if cmd == 'restart' or cmd == 'stop':
+            result = self.stop_daemon()
+            if result:
+                print "Could not stop daemon"
+                # It's ok to continue trying to restart if stop_daemon returns
+                # a 1, otherwise shortcut and return.
+                if cmd == 'restart' and result != 1:
+                    return result
+            if cmd == 'stop':
+                return result
+            self.options.daemon = True
+
+        if cmd == 'start':
+            self.options.daemon = True
+
+        app_name = self.options.app_name
+        vars = self.parse_vars(restvars)
+        if not self._scheme_re.search(app_spec):
+            app_spec = 'config:' + app_spec
+        server_name = self.options.server_name
+        if self.options.server:
+            server_spec = 'egg:PasteScript'
+            assert server_name is None
+            server_name = self.options.server
+        else:
+            server_spec = app_spec
+        base = os.getcwd()
+
+        if getattr(self.options, 'daemon', False):
+            if not self.options.pid_file:
+                self.options.pid_file = 'paster.pid'
+            if not self.options.log_file:
+                self.options.log_file = 'paster.log'
+
+        # Ensure the log file is writeable
+        if self.options.log_file:
+            try:
+                writeable_log_file = open(self.options.log_file, 'a')
+            except IOError, ioe:
+                msg = 'Error: Unable to write to log file: %s' % ioe
+                raise BadCommand(msg)
+            writeable_log_file.close()
+
+        # Ensure the pid file is writeable
+        if self.options.pid_file:
+            try:
+                writeable_pid_file = open(self.options.pid_file, 'a')
+            except IOError, ioe:
+                msg = 'Error: Unable to write to pid file: %s' % ioe
+                raise BadCommand(msg)
+            writeable_pid_file.close()
+
+        if getattr(self.options, 'daemon', False):
+            try:
+                self.daemonize()
+            except DaemonizeException, ex:
+                if self.verbose > 0:
+                    print str(ex)
+                return
+
+        if (self.options.monitor_restart
+            and not os.environ.get(self._monitor_environ_key)):
+            return self.restart_with_monitor()
+
+        if self.options.pid_file:
+            self.record_pid(self.options.pid_file)
+
+        if self.options.log_file:
+            stdout_log = LazyWriter(self.options.log_file, 'a')
+            sys.stdout = stdout_log
+            sys.stderr = stdout_log
+            logging.basicConfig(stream=stdout_log)
+
+        log_fn = app_spec
+        if log_fn.startswith('config:'):
+            log_fn = app_spec[len('config:'):]
+        elif log_fn.startswith('egg:'):
+            log_fn = None
+        if log_fn:
+            log_fn = os.path.join(base, log_fn)
+            self.logging_file_config(log_fn)
+
+        server = loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars)
+
+        app = loadapp( app_spec, name=app_name, relative_to=base, global_conf=vars)
+
+        if self.verbose > 0:
+            if hasattr(os, 'getpid'):
+                msg = 'Starting server in PID %i.' % os.getpid()
+            else:
+                msg = 'Starting server.'
+            print msg
+
+        def serve():
+            try:
+                server(app)
+            except (SystemExit, KeyboardInterrupt), e:
+                if self.verbose > 1:
+                    raise
+                if str(e):
+                    msg = ' '+str(e)
+                else:
+                    msg = ''
+                print 'Exiting%s (-v to see traceback)' % msg
+
+        if jython_monitor:
+            # JythonMonitor has to be ran from the main thread
+            threading.Thread(target=serve).start()
+            print 'Starting Jython file monitor'
+            jython_monitor.periodic_reload()
+        else:
+            serve()
+
+    def daemonize(self):
+        pid = live_pidfile(self.options.pid_file)
+        if pid:
+            raise DaemonizeException(
+                "Daemon is already running (PID: %s from PID file %s)"
+                % (pid, self.options.pid_file))
+
+        if self.verbose > 0:
+            print 'Entering daemon mode'
+        pid = os.fork()
+        if pid:
+            # The forked process also has a handle on resources, so we
+            # *don't* want proper termination of the process, we just
+            # want to exit quick (which os._exit() does)
+            os._exit(0)
+        # Make this the session leader
+        os.setsid()
+        # Fork again for good measure!
+        pid = os.fork()
+        if pid:
+            os._exit(0)
+
+        # @@: Should we set the umask and cwd now?
+
+        import resource  # Resource usage information.
+        maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
+        if (maxfd == resource.RLIM_INFINITY):
+            maxfd = MAXFD
+        # Iterate through and close all file descriptors.
+        for fd in range(0, maxfd):
+            try:
+                os.close(fd)
+            except OSError:  # ERROR, fd wasn't open to begin with (ignored)
+                pass
+
+        if (hasattr(os, "devnull")):
+            REDIRECT_TO = os.devnull
+        else:
+            REDIRECT_TO = "/dev/null"
+        os.open(REDIRECT_TO, os.O_RDWR)  # standard input (0)
+        # Duplicate standard input to standard output and standard error.
+        os.dup2(0, 1)  # standard output (1)
+        os.dup2(0, 2)  # standard error (2)
+
+    def record_pid(self, pid_file):
+        pid = os.getpid()
+        if self.verbose > 1:
+            print 'Writing PID %s to %s' % (pid, pid_file)
+        f = open(pid_file, 'w')
+        f.write(str(pid))
+        f.close()
+        atexit.register(_remove_pid_file, pid, pid_file, self.verbose)
+
+    def stop_daemon(self):
+        pid_file = self.options.pid_file or 'paster.pid'
+        if not os.path.exists(pid_file):
+            print 'No PID file exists in %s' % pid_file
+            return 1
+        pid = read_pidfile(pid_file)
+        if not pid:
+            print "Not a valid PID file in %s" % pid_file
+            return 1
+        pid = live_pidfile(pid_file)
+        if not pid:
+            print "PID in %s is not valid (deleting)" % pid_file
+            try:
+                os.unlink(pid_file)
+            except (OSError, IOError), e:
+                print "Could not delete: %s" % e
+                return 2
+            return 1
+        for j in range(10):
+            if not live_pidfile(pid_file):
+                break
+            import signal
+            os.kill(pid, signal.SIGTERM)
+            time.sleep(1)
+        else:
+            print "failed to kill web process %s" % pid
+            return 3
+        if os.path.exists(pid_file):
+            os.unlink(pid_file)
+        return 0
+
+    def show_status(self):
+        pid_file = self.options.pid_file or 'paster.pid'
+        if not os.path.exists(pid_file):
+            print 'No PID file %s' % pid_file
+            return 1
+        pid = read_pidfile(pid_file)
+        if not pid:
+            print 'No PID in file %s' % pid_file
+            return 1
+        pid = live_pidfile(pid_file)
+        if not pid:
+            print 'PID %s in %s is not running' % (pid, pid_file)
+            return 1
+        print 'Server running in PID %s' % pid
+        return 0
+
+    def restart_with_reloader(self):
+        self.restart_with_monitor(reloader=True)
+
+    def restart_with_monitor(self, reloader=False):
+        if self.verbose > 0:
+            if reloader:
+                print 'Starting subprocess with file monitor'
+            else:
+                print 'Starting subprocess with monitor parent'
+        while 1:
+            args = [self.quote_first_command_arg(sys.executable)] + sys.argv
+            new_environ = os.environ.copy()
+            if reloader:
+                new_environ[self._reloader_environ_key] = 'true'
+            else:
+                new_environ[self._monitor_environ_key] = 'true'
+            proc = None
+            try:
+                try:
+                    _turn_sigterm_into_systemexit()
+                    proc = subprocess.Popen(args, env=new_environ)
+                    exit_code = proc.wait()
+                    proc = None
+                except KeyboardInterrupt:
+                    print '^C caught in monitor process'
+                    if self.verbose > 1:
+                        raise
+                    return 1
+            finally:
+                if (proc is not None
+                    and hasattr(os, 'kill')):
+                    import signal
+                    try:
+                        os.kill(proc.pid, signal.SIGTERM)
+                    except (OSError, IOError):
+                        pass
+
+            if reloader:
+                # Reloader always exits with code 3; but if we are
+                # a monitor, any exit code will restart
+                if exit_code != 3:
+                    return exit_code
+            if self.verbose > 0:
+                print '-'*20, 'Restarting', '-'*20
+
+    def change_user_group(self, user, group):
+        if not user and not group:
+            return
+        import pwd, grp
+        uid = gid = None
+        if group:
+            try:
+                gid = int(group)
+                group = grp.getgrgid(gid).gr_name
+            except ValueError:
+                import grp
+                try:
+                    entry = grp.getgrnam(group)
+                except KeyError:
+                    raise BadCommand(
+                        "Bad group: %r; no such group exists" % group)
+                gid = entry.gr_gid
+        try:
+            uid = int(user)
+            user = pwd.getpwuid(uid).pw_name
+        except ValueError:
+            try:
+                entry = pwd.getpwnam(user)
+            except KeyError:
+                raise BadCommand(
+                    "Bad username: %r; no such user exists" % user)
+            if not gid:
+                gid = entry.pw_gid
+            uid = entry.pw_uid
+        if self.verbose > 0:
+            print 'Changing user to %s:%s (%s:%s)' % (
+                user, group or '(unknown)', uid, gid)
+        if hasattr(os, 'initgroups'):
+            os.initgroups(user, gid)
+        else:
+            os.setgroups([e.gr_gid for e in grp.getgrall()
+                          if user in e.gr_mem] + [gid])
+        if gid:
+            os.setgid(gid)
+        if uid:
+            os.setuid(uid)
+
+class LazyWriter(object):
+
+    """
+    File-like object that opens a file lazily when it is first written
+    to.
+    """
+
+    def __init__(self, filename, mode='w'):
+        self.filename = filename
+        self.fileobj = None
+        self.lock = threading.Lock()
+        self.mode = mode
+
+    def open(self):
+        if self.fileobj is None:
+            self.lock.acquire()
+            try:
+                if self.fileobj is None:
+                    self.fileobj = open(self.filename, self.mode)
+            finally:
+                self.lock.release()
+        return self.fileobj
+
+    def write(self, text):
+        fileobj = self.open()
+        fileobj.write(text)
+        fileobj.flush()
+
+    def writelines(self, text):
+        fileobj = self.open()
+        fileobj.writelines(text)
+        fileobj.flush()
+
+    def flush(self):
+        self.open().flush()
+
+def live_pidfile(pidfile):
+    """(pidfile:str) -> int | None
+    Returns an int found in the named file, if there is one,
+    and if there is a running process with that process id.
+    Return None if no such process exists.
+    """
+    pid = read_pidfile(pidfile)
+    if pid:
+        try:
+            os.kill(int(pid), 0)
+            return pid
+        except OSError, e:
+            if e.errno == errno.EPERM:
+                return pid
+    return None
+
+def read_pidfile(filename):
+    if os.path.exists(filename):
+        try:
+            f = open(filename)
+            content = f.read()
+            f.close()
+            return int(content.strip())
+        except (ValueError, IOError):
+            return None
+    else:
+        return None
+
+def _remove_pid_file(written_pid, filename, verbosity):
+    current_pid = os.getpid()
+    if written_pid != current_pid:
+        # A forked process must be exiting, not the process that
+        # wrote the PID file
+        return
+    if not os.path.exists(filename):
+        return
+    f = open(filename)
+    content = f.read().strip()
+    f.close()
+    try:
+        pid_in_file = int(content)
+    except ValueError:
+        pass
+    else:
+        if pid_in_file != current_pid:
+            print "PID file %s contains %s, not expected PID %s" % (
+                filename, pid_in_file, current_pid)
+            return
+    if verbosity > 0:
+        print "Removing PID file %s" % filename
+    try:
+        os.unlink(filename)
+        return
+    except OSError, e:
+        # Record, but don't give traceback
+        print "Cannot remove PID file: %s" % e
+    # well, at least lets not leave the invalid PID around...
+    try:
+        f = open(filename, 'w')
+        f.write('')
+        f.close()
+    except OSError, e:
+        print 'Stale PID left in file: %s (%e)' % (filename, e)
+    else:
+        print 'Stale PID removed'
+
+
+def ensure_port_cleanup(bound_addresses, maxtries=30, sleeptime=2):
+    """
+    This makes sure any open ports are closed.
+
+    Does this by connecting to them until they give connection
+    refused.  Servers should call like::
+
+        import paste.script
+        ensure_port_cleanup([80, 443])
+    """
+    atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries,
+                    sleeptime=sleeptime)
+
+def _cleanup_ports(bound_addresses, maxtries=30, sleeptime=2):
+    # Wait for the server to bind to the port.
+    import socket
+    import errno
+    for bound_address in bound_addresses:
+        for attempt in range(maxtries):
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            try:
+                sock.connect(bound_address)
+            except socket.error, e:
+                if e.args[0] != errno.ECONNREFUSED:
+                    raise
+                break
+            else:
+                time.sleep(sleeptime)
+        else:
+            raise SystemExit('Timeout waiting for port.')
+        sock.close()
+
+def _turn_sigterm_into_systemexit():
+    """
+    Attempts to turn a SIGTERM exception into a SystemExit exception.
+    """
+    try:
+        import signal
+    except ImportError:
+        return
+    def handle_term(signo, frame):
+        raise SystemExit
+    signal.signal(signal.SIGTERM, handle_term)
+
+# ---- from paste.script.command --------------------------------------
+
+python_version = sys.version.splitlines()[0].strip()
+
+parser = optparse.OptionParser(add_help_option=False,
+                               # version='%s from %s (python %s)'
+                               # % (dist, dist.location, python_version),
+                               usage='%prog [paster_options] COMMAND [command_options]')
+
+parser.add_option(
+    '-h', '--help',
+    action='store_true',
+    dest='do_help',
+    help="Show this help message")
+parser.disable_interspersed_args()
+
+# @@: Add an option to run this in another Python interpreter
+
+commands = {
+    'serve': ServeCommand
+}
+
+def run(args=None):
+    if (not args and
+        len(sys.argv) >= 2
+        and os.environ.get('_') and sys.argv[0] != os.environ['_']
+        and os.environ['_'] == sys.argv[1]):
+        # probably it's an exe execution
+        args = ['exe', os.environ['_']] + sys.argv[2:]
+    if args is None:
+        args = sys.argv[1:]
+    options, args = parser.parse_args(args)
+    options.base_parser = parser
+    if options.do_help:
+        args = ['help'] + args
+    if not args:
+        print 'Usage: %s COMMAND' % sys.argv[0]
+        args = ['help']
+    command_name = args[0]
+    if command_name not in commands:
+        command = NotFoundCommand
+    else:
+        command = commands[command_name]
+    invoke(command, command_name, options, args[1:])
+
+
+def invoke(command, command_name, options, args):
+    try:
+        runner = command(command_name)
+        exit_code = runner.run(args)
+    except BadCommand, e:
+        print e.message
+        exit_code = e.exit_code
+    sys.exit(exit_code)
diff --git a/pulsar/main.py b/pulsar/main.py
index fe27fd510a0f4098ddd9932f56a518ef8e61373e..23609649d8ae402d8415b553e8142a9ca6b53cb4 100644
--- a/pulsar/main.py
+++ b/pulsar/main.py
@@ -38,8 +38,6 @@ except ImportError:
             return options
 
 
-from paste.deploy.loadwsgi import ConfigLoader
-
 log = logging.getLogger(__name__)
 
 REQUIRES_DAEMONIZE_MESSAGE = "Attempted to use Pulsar in daemon mode, but daemonize is unavailable."
@@ -144,6 +142,7 @@ def load_app_configuration(ini_path=None, app_conf_path=None, app_name=None, loc
     """
     """
     if ini_path and local_conf is None:
+        from galaxy.util.pastescript.loadwsgi import ConfigLoader
         local_conf = ConfigLoader(ini_path).app_context(app_name).config()
     local_conf = local_conf or {}
     if app_conf_path is None and "app_config" in local_conf: