[Zope3-checkins] CVS: Zope3/src/zope/app/startup - __init__.py:1.1.2.1 bootstrap.py:1.1.2.1 configure.zcml:1.1.2.1 initzodb.py:1.1.2.1 meta.zcml:1.1.2.1 metaconfigure.py:1.1.2.1 requestfactory.py:1.1.2.1 requestfactoryregistry.py:1.1.2.1 servertype.py:1.1.2.1 servertyperegistry.py:1.1.2.1 simpleregistry.py:1.1.2.1 sitedefinition.py:1.1.2.1 startup.py:1.1.2.1

Jim Fulton jim@zope.com
Mon, 23 Dec 2002 14:32:32 -0500


Update of /cvs-repository/Zope3/src/zope/app/startup
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/app/startup

Added Files:
      Tag: NameGeddon-branch
	__init__.py bootstrap.py configure.zcml initzodb.py meta.zcml 
	metaconfigure.py requestfactory.py requestfactoryregistry.py 
	servertype.py servertyperegistry.py simpleregistry.py 
	sitedefinition.py startup.py 
Log Message:
Initial renaming before debugging

=== Added File Zope3/src/zope/app/startup/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/app/startup/bootstrap.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap code.

This module contains code to bootstrap a Zope3 instance.  For example
it makes sure a root folder exists and creates and configures some
essential services.

$Id: bootstrap.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""
from transaction import get_transaction

from Zope.App.Traversing import traverse, traverseName
from zope.app.publication.zopepublication import ZopePublication
from zope.app.content.folder import RootFolder
from zope.app.services.service import ServiceManager
from zope.app.services.service import \
     ServiceConfiguration
from zope.app.services.hub import ObjectHub
from zope.app.services.event import \
     LocalEventService
from zope.app.services.errorr import \
     ErrorReportingService


def bootstrapInstance(db):
    """Bootstrap a Zope3 instance given a database object.

    This first checks if the root folder exists.  If it exists, nothing
    is changed.  If no root folder exists, one is added, and several
    essential services are added and configured.
    """
    connection = db.open()
    root = connection.root()
    root_folder = root.get(ZopePublication.root_name, None)

    if root_folder is None:
        # Bootstrap code

        root_folder = RootFolder()
        addEssentialServices(root_folder)
        root[ZopePublication.root_name] = root_folder

        get_transaction().commit()

    connection.close()


def addEssentialServices(root_folder):
    """Add essential services.

    XXX This ought to be configurable.  For now, hardcode some
    services we know we all need.
    """
    service_manager = ServiceManager()
    root_folder.setServiceManager(service_manager)
    addService(root_folder, 'Events', LocalEventService)
    addService(root_folder, 'ObjectHub', ObjectHub)
    addService(root_folder, 'ErrorReportingService', ErrorReportingService,
               copy_to_zlog=True)


def addService(root_folder, service_type, service_factory,
               initial_status='Active', **kw):
    """Add and configure a service to the root folder.

    The service is added to the default package and activated.
    This assumes the root folder already has a service manager,
    and that we add at most one service of each type.
    """
    # The code here is complicated by the fact that the registry
    # calls at the end require a fully context-wrapped
    # configuration; hence all the traverse[Name]() calls.
    package_name = ('', '++etc++Services', 'Packages', 'default')
    package = traverse(root_folder, package_name)
    name = service_type + '-1'
    service = service_factory()
    package.setObject(name, service)
    configuration_manager = traverseName(package, 'configure')
    configuration =  ServiceConfiguration(service_type,
                                          package_name + (name,))
    key = configuration_manager.setObject(None, configuration)
    configuration = traverseName(configuration_manager, key)
    configuration.status = initial_status
    # Set additional attributes on the service
    for k, v in kw.iteritems():
        setattr(service, k, v)


=== Added File Zope3/src/zope/app/startup/configure.zcml ===
<zopeConfigure
   xmlns="http://namespaces.zope.org/zope"
   xmlns:startup="http://namespaces.zope.org/startup">


  <startup:registerRequestFactory name="BrowserRequestFactory"
    publication = 
    "Zope.App.ZopePublication.Browser.Publication.BrowserPublication"
    request = "Zope.Publisher.Browser.BrowserRequest." 
  />


  <startup:registerRequestFactory name="XMLRPCRequestFactory" 
    publication = 
    "Zope.App.ZopePublication.XMLRPC.Publication.XMLRPCPublication"
    request = "Zope.Publisher.XMLRPC.XMLRPCRequest." 
  />


  <startup:registerRequestFactory name="VFSRequestFactory"
    publication = 
    "Zope.App.ZopePublication.VFS.Publication.VFSPublication"
    request = "Zope.Publisher.VFS.VFSRequest." 
  />


  <startup:registerServerType 
    name = "Browser"
    factory = "Zope.Server.HTTP.PublisherHTTPServer."
    requestFactory="BrowserRequestFactory"
    logFactory = "Zope.Server.HTTP.CommonHitLogger."
    defaultPort="8080"
    defaultVerbose="true" />


  <startup:registerServerType 
    name = "XML-RPC"
    factory = "Zope.Server.HTTP.PublisherHTTPServer."
    requestFactory="XMLRPCRequestFactory"
    logFactory = "Zope.Server.HTTP.CommonHitLogger."
    defaultPort="8081"
    defaultVerbose="true" />


  <startup:registerServerType 
    name = "FTP"
    factory = "Zope.Server.FTP.PublisherFTPServer."
    requestFactory="VFSRequestFactory"
    logFactory = "Zope.Server.FTP.CommonFTPActivityLogger."
    defaultPort="8021"
    defaultVerbose="true" />


</zopeConfigure>


=== Added File Zope3/src/zope/app/startup/initzodb.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################


=== Added File Zope3/src/zope/app/startup/meta.zcml ===
<zopeConfigure xmlns='http://namespaces.zope.org/zope'>

  <directives namespace="http://namespaces.zope.org/startup">

    <directive name="defineSite"
               attributes="name threads"
               handler="zope.app.startup.metaconfigure.defineSite">

      <subdirective name="useFileStorage"
                    attributes="file" />

      <subdirective name="useMappingStorage" />

      <subdirective name="useLog" attributes="file level" />

      <subdirective name="addServer"
                    attributes="type port verbose logClass" />

    </directive>

    <directive name="registerRequestFactory"
               attributes="name publication request"
               handler="zope.app.startup.metaconfigure.registerRequestFactory"
               />

    <directive name="registerServerType"
               attributes="name publication request"
               handler="zope.app.startup.metaconfigure.registerServerType"
               />

  </directives>

</zopeConfigure>


=== Added File Zope3/src/zope/app/startup/metaconfigure.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
This module handles the :startup directives. 

$Id: metaconfigure.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""

from zope.app.startup.sitedefinition import SiteDefinition
from zope.configuration.action import Action
from zope.app.startup.requestfactory import RequestFactory
import zope.app.startup.requestfactoryregistry
from zope.app.startup.servertype import ServerType
import zope.app.startup.servertyperegistry

defineSite = SiteDefinition


def registerRequestFactory(_context, name, publication, request):
    """ """
    publication = _context.resolve(publication)
    request = _context.resolve(request)
    request_factory = RequestFactory(publication, request)

    return [
        Action(
            discriminator = name,
            callable = RequestFactoryRegistry.registerRequestFactory,
            args = (name, request_factory,),
            )
        ]


def registerServerType(_context, name, factory, requestFactory, logFactory,
                       defaultPort, defaultVerbose):
    """ """
    factory = _context.resolve(factory)
    logFactory = _context.resolve(logFactory)

    if defaultVerbose.lower() == 'true':
        defaultVerbose = 1
    else:
        defaultVerbose = 0

    defaultPort = int(defaultPort)

    server_type = ServerType(name, factory, requestFactory, logFactory,
                             defaultPort, defaultVerbose)
    

    return [
        Action(
            discriminator = name,
            callable = ServerTypeRegistry.registerServerType,
            args = (name, server_type),
            )
        ]



=== Added File Zope3/src/zope/app/startup/requestfactory.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""ctory.py,v 1.1.2.2 2002/04/02 02:20:40 srichter Exp $
"""

from zope.interface import Interface
import copy


class IRequestFactory(Interface):
    """This is a pure read-only interface, since the values are set through
       a ZCML directive and we shouldn't be able to change them.
    """

    def realize(db):
        """Realize the factory by initalizing the publication.

           The method returns the realized object.
        """
        

    def __call__(input_stream, output_steam, env):
        """Call the Request Factory"""





class RequestFactory:
    """This class will generically create RequestFactories. This way I do
       not have to create a method for each Server Type there is.
    """

    __implements__ =  IRequestFactory

    def __init__(self, publication, request):
        """Initialize Request Factory"""
        self._pubFactory = publication
        self._publication = None
        self._request = request


    ############################################################
    # Implementation methods for interface
    # Zope.App.StartUp.RequestFactory.IRequestFactory

    def realize(self, db):
        'See Zope.App.StartUp.RequestFactory.IRequestFactory'
        realized = copy.copy(self)
        realized._publication = realized._pubFactory(db)
        return realized
    

    def __call__(self, input_stream, output_steam, env):
        'See Zope.App.StartUp.RequestFactory.IRequestFactory'
        request = self._request(input_stream, output_steam, env)
        request.setPublication(self._publication)
        return request

    #
    ############################################################


=== Added File Zope3/src/zope/app/startup/requestfactoryregistry.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
$Id: requestfactoryregistry.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""
from zope.app.startup.simpleregistry import SimpleRegistry
from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
from zope.app.startup.requestfactory import IRequestFactory 

class IRequestFactoryRegistry(ISimpleRegistry):
    """
    The RequestFactory Registry manages a list of all the fields
    available in Zope. A registry is useful at this point, since
    fields can be initialized and registered by many places.

    Note that it does not matter whether we have classes or instances as
    fields. If the fields are instances, they must implement
    IInstanceFactory.
    """
    pass


class RequestFactoryRegistry(SimpleRegistry):
    """ """
    __implements__ =  (IRequestFactoryRegistry,)



RequestFactoryRegistry = RequestFactoryRegistry(IRequestFactory)
registerRequestFactory = RequestFactoryRegistry.register
getRequestFactory = RequestFactoryRegistry.get


=== Added File Zope3/src/zope/app/startup/servertype.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""e.py,v 1.1.2.2 2002/04/02 02:20:40 srichter Exp $
"""

from zope.interface import Interface
from zope.app.startup.requestfactoryregistry import getRequestFactory


class IServerType(Interface):
    """This is a pure read-only interface, since the values are set through
       a ZCML directive and we shouldn't be able to change them.
    """

    def create(task_dispatcher, db, port=None, verbose=None):
        """Create the server knowing the port, task dispatcher and the ZODB.
        """


class ServerType:

    __implements__ =  IServerType


    def __init__(self, name, factory, requestFactory, logFactory,
                 defaultPort, defaultVerbose):
        """ """
        self._name = name
        self._factory = factory
        self._requestFactory = requestFactory
        self._logFactory = logFactory
        self._defaultPort = defaultPort
        self._defaultVerbose = defaultVerbose


    ############################################################
    # Implementation methods for interface
    # Zope.App.StartUp.ServerType.IServerType

    def create(self, task_dispatcher, db, port=None, verbose=None):
        'See Zope.App.StartUp.ServerType.IServerType'

        request_factory = getRequestFactory(self._requestFactory)
        request_factory = request_factory.realize(db)

        if port is None:
            port = self._defaultPort

        if verbose is None:
            verbose = self._defaultVerbose

        apply(self._factory,
              (request_factory, self._name, '', port),
              {'task_dispatcher': task_dispatcher,
               'verbose': verbose,
               'hit_log': self._logFactory()})

    #
    ############################################################

        


=== Added File Zope3/src/zope/app/startup/servertyperegistry.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
$Id: servertyperegistry.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""
from zope.app.startup.simpleregistry import SimpleRegistry
from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
from zope.app.startup.servertype import IServerType 

class IServerTypeRegistry(ISimpleRegistry):
    """
    The ServerType Registry manages a list of all the fields
    available in Zope. A registry is useful at this point, since
    fields can be initialized and registered by many places.

    Note that it does not matter whether we have classes or instances as
    fields. If the fields are instances, they must implement
    IInstanceFactory.
    """
    pass


class ServerTypeRegistry(SimpleRegistry):
    """Registry for the various Server types"""
    __implements__ =  (IServerTypeRegistry,)


ServerTypeRegistry = ServerTypeRegistry(IServerType)
registerServerType = ServerTypeRegistry.register
getServerType = ServerTypeRegistry.get


=== Added File Zope3/src/zope/app/startup/simpleregistry.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
$Id: simpleregistry.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""
from zope.configuration.name import resolve
from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
from types import StringTypes, ListType, TupleType
ListTypes = (TupleType, ListType)


class ZopeDuplicateRegistryEntryError(Exception):
    """
    This Error is raised when the user tries to add an object with 
    a name that already exists in the registry. Therefore,
    overwriting is not allowed.
    """

    def __init__(self, name):
        """Initializes Error"""
        self.name = name

    def __str__(self):
        """Returns string representation of Error"""
        return "The name '%s' is already defined in this registry." \
               %self.name


class ZopeIllegalInterfaceError(Exception):
    """This Error is thrown, when the passed object does not implement
    the specified interface."""

    def __init__(self, name, interface):
        """Initalize Error"""
        self.name = name
        self.interface = interface

    def __str__(self):
        """Returns string representation of Error"""
        return ( "The object with name " + self.name + " does not implement "
                 "the interface " + self.interface.__name__ + "." )



class SimpleRegistry:
    """ """

    __implements__ =  (ISimpleRegistry,)


    def __init__(self, interface):
        """Initialize registry"""
        self.objects = {}
        self.interface = interface
        

    def register(self, name, object):
        '''See interface Zope.App.Formulator.ISimpleRegistry.ISimpleRegistry'''

        if name in self.objects.keys():
            raise ZopeDuplicateRegistryEntryError(name)

        # XXX Find the right Interface tools to do that; unfortunately,
        #     I have not found them
        # Check whether the object implements the right interface.
        # Note, that we do *not* know whether the object is an instance
        # or a class (or worse a Persistent class)
        if hasattr(object, '__implements__') and \
               ( self.interface == object.__implements__ or \
                 ( type(object.__implements__) in ListTypes and
                   self.interface in object.__implements__ ) ):
            self.objects[name] = object

        else:
            raise ZopeIllegalInterfaceError(name, self.interface)

        return []


    def get(self, name):
        '''See interface Zope.App.Formulator.ISimpleRegistry.ISimpleRegistry'''
        if name in self.objects.keys():
            return self.objects[name]
        else:
            return None


=== Added File Zope3/src/zope/app/startup/sitedefinition.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""
This module handles the :startup directives. 

$Id: sitedefinition.py,v 1.1.2.1 2002/12/23 19:32:30 jim Exp $
"""

import sys
import logging
import asyncore

# Importing ZLogIntegration redirects asyncore's logging to the
# logging module
from zope.server import ZLogIntegration

# Import Configuration-related classes
from zope.configuration.action import Action
from zope.interfaces.configuration import INonEmptyDirective
from zope.interfaces.configuration import ISubdirectiveHandler

from zope.app.startup.servertyperegistry import getServerType

# Import Undo-related classes 
from zope.component import getService
from zope.app.undo import ZODBUndoManager
from zope.app.interfaces.undo import IUndoManager
from zope.app.content.folder import RootFolder
from zope.server.taskthreads import ThreadedTaskDispatcher
from zope.app.publication.zopepublication import ZopePublication

from zodb.code.module import PersistentModuleImporter

DEFAULT_STORAGE_FILE = 'Data.fs'
DEFAULT_LOG_FILE = 'STDERR'
DEFAULT_LOG_LEVEL = 'INFO'


class SiteDefinition:

    __class_implements__ = INonEmptyDirective    
    __implements__ = ISubdirectiveHandler

    # Some special file names for log files
    _special_log_files = {'STDERR': sys.stderr,
                          'STDOUT': sys.stdout}

    # Mapping from log level names to numeric log levels
    _log_levels = {
        'CRITICAL' : logging.CRITICAL,
        'ERROR' : logging.ERROR,
        'WARN' : logging.WARN,
        'INFO' : logging.INFO,
        'DEBUG' : logging.DEBUG,
        'NOTSET' : logging.NOTSET,
        }

    
    def __init__(self, _context, name="default", threads=4):
        """Initialize is called when defineSite directive is invoked."""
        self._name = name
        self._threads = int(threads)

        self._zodb = None
        self.useLog(_context)
        self._servers = {}

        self._started = 0

    def close(self):
        if self._zodb is not None:
            self._zodb.close()
            self._zodb = None

    def useFileStorage(self, _context, file=DEFAULT_STORAGE_FILE):
        """Lets you specify the ZODB to use."""
        from zodb.storage.file import DB
        if self._zodb is not None:
            raise RuntimeError("Database already open")
        self._zodb = DB(file)
        return []


    def useMappingStorage(self, _context):
        """Lets you specify the ZODB to use."""
        from zodb.storage.mapping import DB
        if self._zodb is not None:
            raise RuntimeError("Database already open")
        self._zodb = DB()
        return []


    def useLog(self, _context, file=DEFAULT_LOG_FILE, level=DEFAULT_LOG_LEVEL):
        """Lets you specify the log file and level to use"""

        # Translate the level to logging
        loglevel = self._log_levels.get(level.upper())
        if loglevel is None:
            raise ValueError, "unknown log level %r" % level

        # Get the root logger and set its logging level
        root = logging.root
        root.setLevel(loglevel)

        # Remove previous handlers
        for h in root.handlers[:]:
            root.removeHandler(h)

        # Create the new handler
        if file in self._special_log_files.keys():
            file = self._special_log_files[file]
            handler = logging.StreamHandler(file)
        else:
            handler = logging.FileHandler(file)

        # Create a standard Zope-style formatter and set it
        formatter = logging.Formatter(
            "------\n"
            "%(asctime)s %(levelname)s %(name)s %(message)s",
            datefmt="%Y-%m-%dT%H:%M:%S")
        handler.setFormatter(formatter)

        # Set the handler
        root.addHandler(handler)

        # Return empty sequence to satisfy API
        return []


    def addServer(self, _context, type, port=None, verbose=None):
        """Add a new server for this site."""

        if port is not None:
            port = int(port)

        if verbose is not None:
            if verbose.lower() == 'true': verbose = 1
            else: verbose = 0

        if type is not None:
            self._servers[type] = {'port': port,
                                   'verbose': verbose}
        else:
            sys.stderr.out('Warning: Server of Type %s does not exist. ' +
                           'Directive neglected.') 
        return []


    def start(self):
        """Now start all the servers"""

        sys.stderr.write('\nStarting Site: %s\n\n' %self._name)

        sys.setcheckinterval(120)

        # setup undo fnctionality
        getService(None,"Utilities").provideUtility(
            IUndoManager,
            ZODBUndoManager(self._zodb)
            )

        # Setup the task dispatcher
        td = ThreadedTaskDispatcher()
        td.setThreadCount(self._threads)
        
        # check whether a root was already specified for this ZODB; if
        # not create one.
        self._initDB()

        # Start the servers
        for type, server_info in self._servers.items():

            server = getServerType(type)
            server.create(td, self._zodb, server_info['port'],
                          server_info['verbose'])

    def _initDB(self):
        """Initialize the ZODB and persistence module importer."""

        from Zope.App.StartUp import bootstrap
        bootstrap.bootstrapInstance(self._zodb)

        imp = PersistentModuleImporter()
        imp.install()


    def __call__(self):
        "Handle empty/simple declaration."
        return [ Action(discriminator = 'Start Servers',
                        callable = self.start,
                        args = ()),
                 ]


=== Added File Zope3/src/zope/app/startup/startup.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################