[Zope3-checkins] CVS: Zope3/src/zope/app/process - __init__.py:1.2 bootstrap.py:1.2 component.xml:1.2 configure.zcml:1.2 datatypes.py:1.2 event.py:1.2 loghandlers.py:1.2 main.py:1.2 meta.zcml:1.2 metaconfigure.py:1.2 requestfactory.py:1.2 requestfactoryregistry.py:1.2 schema.xml:1.2 server.py:1.2 servertype.py:1.2 servertyperegistry.py:1.2 simpleregistry.py:1.2

Fred L. Drake, Jr. fred@zope.com
Wed, 25 Jun 2003 11:30:04 -0400


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

Added Files:
	__init__.py bootstrap.py component.xml configure.zcml 
	datatypes.py event.py loghandlers.py main.py meta.zcml 
	metaconfigure.py requestfactory.py requestfactoryregistry.py 
	schema.xml server.py servertype.py servertyperegistry.py 
	simpleregistry.py 
Log Message:
Merged from fdrake-zconfig-in-zope-3-branch:
Replace zope.app.startup with zope.app.process; these contain mostly the same
code, but zope.app.process supports the new startup events instead of calling
various initialization routines directly, and supports configuration of the
appserver using ZConfig.


=== Zope3/src/zope/app/process/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:03 2003
+++ Zope3/src/zope/app/process/__init__.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1 @@
+# Make this a package.


=== Zope3/src/zope/app/process/bootstrap.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:03 2003
+++ Zope3/src/zope/app/process/bootstrap.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,133 @@
+##############################################################################
+#
+# 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$
+"""
+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.servicenames import HubIds
+from zope.app.services.servicenames import EventPublication, EventSubscription
+from zope.app.services.servicenames import ErrorLogging
+from zope.app.services.service import ServiceManager
+from zope.app.services.service import ServiceRegistration
+from zope.app.services.hub import ObjectHub
+from zope.app.services.event import EventService
+from zope.app.services.error import ErrorReportingService
+from zope.app.services.principalannotation import PrincipalAnnotationService
+from zope.proxy import removeAllProxies
+from zope.app.event import publish
+from zope.app.event.objectevent import ObjectCreatedEvent
+from zope.app.event import function
+
+def bootstrapInstance(event):
+    """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.
+    """
+    db = event.database
+    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
+
+        publish(root_folder, ObjectCreatedEvent(root_folder))
+        get_transaction().commit()
+
+    connection.close()
+
+
+bootstrapInstance = function.Subscriber(bootstrapInstance)
+
+
+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)
+
+    # The EventService class implements two services
+    name = addConfigureService(root_folder, EventPublication, EventService)
+    configureService(root_folder, EventSubscription, name)
+
+    # Add the HubIds service, which subscribes itself to the event service
+    name = addService(root_folder, HubIds, ObjectHub)
+    configureService(root_folder, HubIds, name)
+
+    # Sundry other services
+    addConfigureService(root_folder, ErrorLogging,
+                        ErrorReportingService, copy_to_zlog=True)
+    addConfigureService(root_folder, 'PrincipalAnnotation',
+                        PrincipalAnnotationService)
+
+
+def addConfigureService(root_folder, service_type, service_factory, **kw):
+    """Add and configure a service to the root folder."""
+    name = addService(root_folder, service_type, service_factory, **kw)
+    configureService(root_folder, service_type, name)
+    return name
+
+def addService(root_folder, service_type, service_factory, **kw):
+    """Add 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.
+
+    Returns the name of the service implementation in the default package.
+    """
+    # The code here is complicated by the fact that the registry
+    # calls at the end require a fully context-wrapped
+    # registration; hence all the traverse() and traverseName() calls.
+    package_name = '/++etc++site/default'
+    package = traverse(root_folder, package_name)
+    name = service_type + '-1'
+    service = service_factory()
+    service = removeAllProxies(service)
+    package.setObject(name, service)
+
+    # Set additional attributes on the service
+    for k, v in kw.iteritems():
+        setattr(service, k, v)
+    return name
+
+def configureService(root_folder, service_type, name, initial_status='Active'):
+    """Configure a service in the root folder."""
+    package_name = '/++etc++site/default'
+    package = traverse(root_folder, package_name)
+    registration_manager = package.getRegistrationManager()
+    registration =  ServiceRegistration(service_type,
+                                        name,
+                                        registration_manager)
+    key = registration_manager.setObject("", registration)
+    registration = traverseName(registration_manager, key)
+    registration.status = initial_status


=== Zope3/src/zope/app/process/component.xml 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:03 2003
+++ Zope3/src/zope/app/process/component.xml	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,67 @@
+<component prefix="zope.app.process.datatypes">
+
+  <abstracttype name="logging.loghandler"/>
+
+  <sectiontype name="logging.base-log-handler">
+    <description>
+      Base type for most log handlers.  This is cannot be used as a
+      loghandler directly since it doesn't implement the loghandler
+      abstract section type.
+    </description>
+    <key name="dateformat" default="%Y-%m-%dT%H:%M:%S"/>
+    <key name="level" default="notset" datatype=".logging_level"/>
+  </sectiontype>
+
+  <sectiontype name="logfile" datatype=".FileHandlerFactory"
+               implements="logging.loghandler"
+               extends="logging.base-log-handler">
+    <key name="path" required="yes"/>
+    <key name="format"
+         default="------\n%(asctime)s %(levelname)s %(name)s %(message)s"
+         datatype=".log_format"/>
+  </sectiontype>
+
+  <sectiontype name="syslog" datatype=".SyslogHandlerFactory"
+               implements="logging.loghandler"
+               extends="logging.base-log-handler">
+    <key name="facility" default="user" datatype=".syslog_facility"/>
+    <key name="address" datatype="socket-address" default="localhost:514"/>
+    <key name="format" default="%(message)s"
+         datatype=".log_format"/>
+  </sectiontype>
+
+  <sectiontype name="win32-eventlog" datatype=".Win32EventLogFactory"
+               implements="logging.loghandler"
+               extends="logging.base-log-handler">
+    <key name="appname" default="Zope"/>
+    <key name="format" default="%(message)s"
+         datatype=".log_format"/>
+  </sectiontype>
+
+  <sectiontype name="http-logger" datatype=".HTTPHandlerFactory"
+               implements="logging.loghandler"
+               extends="logging.base-log-handler">
+    <key name="url" default="http://localhost/" datatype=".http_handler_url"/>
+    <key name="method" default="GET" datatype=".get_or_post"/>
+    <key name="format" default="%(asctime)s %(message)s"
+         datatype=".log_format"/>
+  </sectiontype>
+
+  <sectiontype name="email-notifier" datatype=".SMTPHandlerFactory"
+               implements="logging.loghandler"
+               extends="logging.base-log-handler">
+    <key name="from" required="yes" attribute="fromaddr"/>
+    <multikey name="to" required="yes" attribute="toaddrs"/>
+    <key name="subject" default="Message from Zope"/>
+    <key name="smtp-server" default="localhost" datatype="inet-address"/>
+    <key name="format" default="%(asctime)s %(message)s"
+         datatype=".log_format"/>
+  </sectiontype>
+
+
+  <sectiontype name="eventlog" datatype=".EventLogFactory">
+     <key name="level" datatype=".logging_level" default="info"/>
+     <multisection type="logging.loghandler" attribute="handlers" name="*"/>
+  </sectiontype>
+
+</component>


=== Zope3/src/zope/app/process/configure.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:03 2003
+++ Zope3/src/zope/app/process/configure.zcml	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,73 @@
+<zopeConfigure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:startup="http://namespaces.zope.org/startup"
+   xmlns:event="http://namespaces.zope.org/event">
+
+
+  <startup:registerRequestFactory 
+    name="HTTPRequestFactory"
+    factory="zope.app.publication.httpfactory"
+  />
+
+
+  <startup:registerRequestFactory name="BrowserRequestFactory"
+    publication = 
+    "zope.app.publication.browser.BrowserPublication"
+    request = "zope.publisher.browser.BrowserRequest" 
+  />
+
+
+  <startup:registerRequestFactory name="XMLRPCRequestFactory" 
+    publication = 
+    "zope.app.publication.xmlrpc.XMLRPCPublication"
+    request = "zope.publisher.xmlrpc.XMLRPCRequest" 
+  />
+
+
+  <startup:registerRequestFactory name="FTPRequestFactory"
+    publication = "zope.app.publication.ftp.FTPPublication"
+    request = "zope.publisher.ftp.FTPRequest" 
+  />
+
+
+  <startup:registerServerType 
+    name = "HTTP"
+    factory = "zope.server.http.publisherhttpserver.PublisherHTTPServer"
+    requestFactory="HTTPRequestFactory"
+    logFactory = "zope.server.http.commonhitlogger.CommonHitLogger"
+    defaultPort="8080"
+    defaultVerbose="true" />
+
+  <startup:registerServerType 
+    name = "Browser"
+    factory = "zope.server.http.publisherhttpserver.PublisherHTTPServer"
+    requestFactory="BrowserRequestFactory"
+    logFactory = "zope.server.http.commonhitlogger.CommonHitLogger"
+    defaultPort="8080"
+    defaultVerbose="true" />
+
+
+  <startup:registerServerType 
+    name = "XML-RPC"
+    factory = "zope.server.http.publisherhttpserver.PublisherHTTPServer"
+    requestFactory="XMLRPCRequestFactory"
+    logFactory = "zope.server.http.commonhitlogger.CommonHitLogger"
+    defaultPort="8081"
+    defaultVerbose="true" />
+
+
+  <startup:registerServerType 
+    name = "FTP"
+    factory = "zope.server.ftp.publisher.PublisherFTPServer"
+    requestFactory="FTPRequestFactory"
+    logFactory = "zope.server.ftp.logger.CommonFTPActivityLogger"
+    defaultPort="8021"
+    defaultVerbose="true" />
+
+
+  <event:subscribe
+    subscriber=".bootstrap.bootstrapInstance"
+    event_types="zope.app.interfaces.event.IDatabaseOpenedEvent"
+    />
+
+</zopeConfigure>


=== Zope3/src/zope/app/process/datatypes.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:03 2003
+++ Zope3/src/zope/app/process/datatypes.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,249 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+
+"""ZConfig datatypes for logging support."""
+
+import sys
+
+# log-related datatypes
+
+_logging_levels = {
+    "critical": 50,
+    "fatal": 50,
+    "error": 40,
+    "warn": 30,
+    "warning": 30,
+    "info": 20,
+    "blather": 15,
+    "debug": 10,
+    "trace": 5,
+    "all": 1,
+    "notset": 0,
+    }
+
+def logging_level(value):
+    s = str(value).lower()
+    if _logging_levels.has_key(s):
+        return _logging_levels[s]
+    else:
+        v = int(s)
+        if v < 0 or v > 50:
+            raise ValueError("log level not in range: " + `v`)
+        return v
+
+_log_format_variables = {
+    'name': '',
+    'levelno': '3',
+    'levelname': 'DEBUG',
+    'pathname': 'apath',
+    'filename': 'afile',
+    'module': 'amodule',
+    'lineno': 1,
+    'created': 1.1,
+    'asctime': 'atime',
+    'msecs': 1,
+    'relativeCreated': 1,
+    'thread': 1,
+    'message': 'amessage',
+    }
+
+def log_format(value):
+    value = ctrl_char_insert(value)
+    try:
+        # Make sure the format string uses only names that will be
+        # provided, and has reasonable type flags for each, and does
+        # not expect positional args.
+        value % _log_format_variables
+    except (ValueError, KeyError):
+        raise ValueError, 'Invalid log format string %s' % value
+    return value
+
+_control_char_rewrites = {r'\n': '\n', r'\t': '\t', r'\b': '\b',
+                          r'\f': '\f', r'\r': '\r'}.items()
+
+def ctrl_char_insert(value):
+    for pattern, replacement in _control_char_rewrites:
+        value = value.replace(pattern, replacement)
+    return value
+
+
+_marker = object()
+
+class Factory:
+    """Generic wrapper for instance construction.
+
+    Calling the factory causes the instance to be created if it hasn't
+    already been created, and returns the object.  Calling the factory
+    multiple times returns the same object.
+
+    The instance is created using the factory's create() method, which
+    must be overriden by subclasses.
+    """
+    def __init__(self):
+        self.instance = _marker
+
+    def __call__(self):
+        if self.instance is _marker:
+            self.instance = self.create()
+        return self.instance
+
+    def create(self):
+        raise NotImplementedError("subclasses need to override create()")
+
+
+class HandlerFactory(Factory):
+    def __init__(self, section):
+        Factory.__init__(self)
+        self.section = section
+
+    def create_loghandler(self):
+        raise NotImplementedError(
+            "subclasses must override create_loghandler()")
+
+    def create(self):
+        import logging
+        logger = self.create_loghandler()
+        logger.setFormatter(logging.Formatter(self.section.format,
+                                              self.section.dateformat))
+        logger.setLevel(self.section.level)
+        return logger
+
+class FileHandlerFactory(HandlerFactory):
+    def create_loghandler(self):
+        # XXX this implementation should change
+        from zope.app.process.loghandlers import StreamHandler, FileHandler
+        path = self.section.path
+        if path == "STDERR":
+            return StreamHandler(sys.stderr)
+        if path == "STDOUT":
+            return StreamHandler(sys.stdout)
+        return FileHandler(path)
+
+_syslog_facilities = {
+    "auth": 1,
+    "authpriv": 1,
+    "cron": 1,
+    "daemon": 1,
+    "kern": 1,
+    "lpr": 1,
+    "mail": 1,
+    "news": 1,
+    "security": 1,
+    "syslog": 1,
+    "user": 1,
+    "uucp": 1,
+    "local0": 1,
+    "local1": 1,
+    "local2": 1,
+    "local3": 1,
+    "local4": 1,
+    "local5": 1,
+    "local6": 1,
+    "local7": 1,
+    }
+
+def syslog_facility(value):
+    value = value.lower()
+    if not _syslog_facilities.has_key(value):
+        L = _syslog_facilities.keys()
+        L.sort()
+        raise ValueError("Syslog facility must be one of " + ", ".join(L))
+    return value
+
+class SyslogHandlerFactory(HandlerFactory):
+    def create_loghandler(self):
+        from zope.app.process.loghandlers import SysLogHandler
+        return SysLogHandler(self.section.address.address,
+                             self.section.facility)
+
+class Win32EventLogFactory(HandlerFactory):
+    def create_loghandler(self):
+        from zope.app.process.loghandlers import Win32EventLogHandler
+        return Win32EventLogHandler(self.section.appname)
+
+def http_handler_url(value):
+    import urlparse
+    scheme, netloc, path, param, query, fragment = urlparse.urlparse(value)
+    if scheme != 'http':
+        raise ValueError, 'url must be an http url'
+    if not netloc:
+        raise ValueError, 'url must specify a location'
+    if not path:
+        raise ValueError, 'url must specify a path'
+    q = []
+    if param:
+        q.append(';')
+        q.append(param)
+    if query:
+        q.append('?')
+        q.append(query)
+    if fragment:
+        q.append('#')
+        q.append(fragment)
+    return (netloc, path + ''.join(q))
+
+def get_or_post(value):
+    value = value.upper()
+    if value not in ('GET', 'POST'):
+        raise ValueError('method must be "GET" or "POST", instead received: '
+                         + repr(value))
+    return value
+
+class HTTPHandlerFactory(HandlerFactory):
+    def create_loghandler(self):
+        from zope.app.process.loghandlers import HTTPHandler
+        host, selector = self.section.url
+        return HTTPHandler(host, selector, self.section.method)
+
+class SMTPHandlerFactory(HandlerFactory):
+    def create_loghandler(self):
+        from zope.app.process.loghandlers import SMTPHandler
+        host, port = self.section.smtp_server
+        if not port:
+            mailhost = host
+        else:
+            mailhost = host, port
+        return SMTPHandler(mailhost, self.section.fromaddr,
+                           self.section.toaddrs, self.section.subject)
+
+
+class EventLogFactory(Factory):
+    """
+    A wrapper used to create loggers while delaying actual logger
+    instance construction.  We need to do this because we may
+    want to reference a logger before actually instantiating it (for example,
+    to allow the app time to set an effective user).
+    An instance of this wrapper is a callable which, when called, returns a
+    logger object.
+    """
+    def __init__(self, section):
+        Factory.__init__(self)
+        self.level = section.level
+        self.handler_factories = section.handlers
+
+    def create(self):
+        # set the logger up
+        import logging
+        logger = logging.getLogger()
+        logger.handlers = []
+        if self.level == logging.NOTSET:
+            self.level = logging_level("all")
+        logger.setLevel(self.level)
+        if self.handler_factories:
+            for factory in self.handler_factories:
+                logger.addHandler(factory())
+        else:
+            from zope.app.process.loghandlers import NullHandler
+            logger.addHandler(NullHandler())
+        return logger


=== Zope3/src/zope/app/process/event.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/event.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,31 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Process-lifetime related events.
+
+$Id$
+"""
+
+from zope.app.interfaces import event
+from zope.interface import implements
+
+
+class DatabaseOpened:
+    implements(event.IDatabaseOpenedEvent)
+
+    def __init__(self, database):
+        self.database = database
+
+
+class ProcessStarting:
+    implements(event.IProcessStartingEvent)


=== Zope3/src/zope/app/process/loghandlers.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/loghandlers.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,79 @@
+##############################################################################
+#
+# Copyright (c) 2001 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.
+#
+##############################################################################
+
+"""Handlers which can plug into a PEP 282 logger."""
+
+import sys
+
+from logging import Handler, StreamHandler
+from logging.handlers import SysLogHandler
+from logging.handlers import HTTPHandler, SMTPHandler
+from logging.handlers import NTEventLogHandler as Win32EventLogHandler
+
+
+class FileHandler(StreamHandler):
+    """File handler supporting the traditional way of reopening logs."""
+
+    def __init__(self, filename, mode="a"):
+        StreamHandler.__init__(self, open(filename, mode))
+        self.baseFilename = filename
+        self.mode = mode
+
+    def close(self):
+        self.stream.close()
+
+    def reopen(self):
+        self.close()
+        self.stream = open(self.baseFilename, self.mode)
+
+
+class NullHandler(Handler):
+    """A null handler.  Does nothing."""
+
+    def emit(self, record):
+        pass
+
+    def handle(self, record):
+        pass
+
+
+class StartupHandler(Handler):
+    """Handler which dumps messages to a stream and buffers them.
+
+    The intention is that the messages eventually will be flushed to
+    another handler.  This is useful at startup before we can know
+    that we can safely write to a configuration-specified handler.
+    """
+    def __init__(self, stream=None):
+        Handler.__init__(self)
+        if not stream:
+            stream = sys.stderr
+        self.stream = stream
+        self.buffer = []
+
+    def emit(self, record):
+        try:
+            self.buffer.append(record)
+            msg = self.format(record)
+            self.stream.write("%s\n" % msg)
+            self.flush()
+        except:
+            self.handleError()
+
+    def flush(self):
+        self.stream.flush()
+
+    def flushBufferTo(self, target):
+        for record in self.buffer:
+            target.handle(record)
+        self.buffer = []


=== Zope3/src/zope/app/process/main.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/main.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""Functions that control how the Zope appserver knits itself together.
+
+$Id$
+"""
+
+import logging
+import os
+import sys
+import time
+
+from zdaemon import zdoptions
+
+from zodb.zeo import threadedasync
+
+from zope.app import config
+from zope.app.event import publish
+from zope.app.process import event
+from zope.server.taskthreads import ThreadedTaskDispatcher
+
+CONFIG_FILENAME = "zope.conf"
+
+
+class ZopeOptions(zdoptions.ZDOptions):
+
+    logsectionname = None
+
+    def default_configfile(self):
+        dir = os.path.normpath(
+            os.path.join(os.path.dirname(__file__),
+                         os.pardir, os.pardir, os.pardir, os.pardir))
+        for filename in [CONFIG_FILENAME, CONFIG_FILENAME + ".in"]:
+            filename = os.path.join(dir, filename)
+            if os.path.isfile(filename):
+                return filename
+        return None
+
+
+def main(args=None):
+    # Record start times (real time and CPU time)
+    t0 = time.time()
+    c0 = time.clock()
+
+    setup(args)
+
+    t1 = time.time()
+    c1 = time.clock()
+    logging.info("Startup time: %.3f sec real, %.3f sec CPU", t1-t0, c1-c0)
+
+    run()
+    sys.exit(0)
+
+
+def run():
+    try:
+        threadedasync.loop()
+    except KeyboardInterrupt:
+        # Exit without spewing an exception.
+        pass
+
+
+def setup(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    options = ZopeOptions()
+    options.schemadir = os.path.dirname(os.path.abspath(__file__))
+    options.realize(args)
+    options = options.configroot
+
+    sys.setcheckinterval(options.check_interval)
+
+    options.eventlog()
+
+    config(options.site_definition)
+
+    db = options.database.open()
+
+    publish(None, event.DatabaseOpened(db))
+
+    task_dispatcher = ThreadedTaskDispatcher()
+    task_dispatcher.setThreadCount(options.threads)
+
+    for server in options.servers:
+        server.create(task_dispatcher, db)
+
+    publish(None, event.ProcessStarting())


=== Zope3/src/zope/app/process/meta.zcml 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/meta.zcml	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,17 @@
+<zopeConfigure xmlns='http://namespaces.zope.org/zope'>
+
+  <directives namespace="http://namespaces.zope.org/startup">
+
+    <directive name="registerRequestFactory"
+               attributes="name publication request"
+               handler="zope.app.process.metaconfigure.registerRequestFactory"
+               />
+
+    <directive name="registerServerType"
+               attributes="name publication request"
+               handler="zope.app.process.metaconfigure.registerServerType"
+               />
+
+  </directives>
+
+</zopeConfigure>


=== Zope3/src/zope/app/process/metaconfigure.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/metaconfigure.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,72 @@
+##############################################################################
+#
+# 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$
+"""
+
+from zope.configuration.action import Action
+from zope.app.process import requestfactoryregistry
+from zope.app.process import servertyperegistry
+from zope.app.process.requestfactory import RequestFactory
+from zope.app.process.servertype import ServerType
+
+
+def registerRequestFactory(_context, name, request=None, publication=None,
+                           factory=None):
+
+    if factory:
+        if request or publication:
+            raise ValuesError(
+                """Can't provide a request or publication (factory) if you
+                provide a (request) factory""")
+        request_factory = _context.resolve(factory)
+
+    else:
+        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 = True
+    else:
+        defaultVerbose = False
+
+    defaultPort = int(defaultPort)
+
+    server_type = ServerType(name, factory, requestFactory, logFactory,
+                             defaultPort, defaultVerbose)
+
+    return [
+        Action(
+            discriminator = name,
+            callable = servertyperegistry.registerServerType,
+            args = (name, server_type),
+            )
+        ]


=== Zope3/src/zope/app/process/requestfactory.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/requestfactory.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,46 @@
+##############################################################################
+#
+# 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 $
+"""
+
+import copy
+from zope.app.interfaces.startup import IRequestFactory
+from zope.interface import implements
+
+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
+
+
+    def realize(self, db):
+        'See IRequestFactory'
+        realized = copy.copy(self)
+        realized._publication = realized._pubFactory(db)
+        return realized
+
+
+    def __call__(self, input_stream, output_steam, env):
+        'See IRequestFactory'
+        request = self._request(input_stream, output_steam, env)
+        request.setPublication(self._publication)
+        return request


=== Zope3/src/zope/app/process/requestfactoryregistry.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/requestfactoryregistry.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# 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$
+"""
+from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
+from zope.app.process.simpleregistry import SimpleRegistry
+from zope.app.interfaces.startup import IPublicationRequestFactoryFactory
+from zope.interface import implements
+
+
+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.
+    """
+
+
+class RequestFactoryRegistry(SimpleRegistry):
+    implements(IRequestFactoryRegistry)
+
+
+RequestFactoryRegistry = RequestFactoryRegistry(
+    IPublicationRequestFactoryFactory)
+
+registerRequestFactory = RequestFactoryRegistry.register
+getRequestFactory = RequestFactoryRegistry.get
+
+# Register our cleanup with Testing.CleanUp to make writing unit tests simpler.
+from zope.testing.cleanup import addCleanUp
+addCleanUp(RequestFactoryRegistry._clear)
+del addCleanUp


=== Zope3/src/zope/app/process/schema.xml 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/schema.xml	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,68 @@
+<schema>
+  <description>
+    Zope 3 configuration schema.
+
+    This schema describes the configuration options available to a
+    site administrator via the zope.conf configuration file.
+  </description>
+
+  <!-- database and storage types -->
+  <import package="zodb" />
+
+  <!-- logging configuration -->
+  <import package="zope.app.process" />
+
+  <sectiontype name="server" datatype="zope.app.process.server.ServerFactory">
+    <key name="type" required="yes" />
+    <key name="address" datatype="inet-address" />
+    <key name="verbose" datatype="boolean" />
+  </sectiontype>
+
+  <section type="zodb.database" name="*" required="yes"
+           attribute="database">
+    <description>
+      The main application database that should be used.
+    </description>
+  </section>
+
+  <section type="eventlog" attribute="eventlog" name="*">
+    <description>
+      Configuration for the event log.
+    </description>
+  </section>
+
+  <multisection type="server" name="*" attribute="servers" />
+
+  <key name="site-definition" default="site.zcml">
+    <description>
+      The name of the top-level ZCML file that defines the component
+      configuration used for this site.
+    </description>
+  </key>
+
+  <key name="interrupt-check-interval" datatype="integer" default="120"
+       attribute="check_interval">
+    <description>
+      Value passed to Python's sys.setcheckinterval() function.
+
+      This integer value determines how often the interpreter checks
+      for periodic things such as thread switches and signal handlers.
+      Setting it to a larger value may increase performance for
+      programs using threads.  Setting it to a value &lt;= 0 checks every
+      virtual instruction, maximizing responsiveness as well as
+      overhead.
+    </description>
+  </key>
+
+  <key name="threads" datatype="integer" default="4">
+    <description>
+      The number of threads which should be used to serve requests.
+
+      The threads are placed in a pool and are used to serve requests
+      received from the servers configured using &lt;server&gt;
+      sections.  This does not constrain the total number of threads
+      used by the application server; additional threads may be used
+      for internal purposes.
+    </description>
+  </key>
+</schema>


=== Zope3/src/zope/app/process/server.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/server.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+
+"""Datatype for a <server> section in a Zope 3 configuration file.
+
+This is called by the ZConfig machinery while processing a configuration.
+
+$Id$
+"""
+
+from zope.app.process.servertyperegistry import getServerType
+
+
+class ServerFactory:
+    """Factory for server objects.
+
+    The factories are part of the configuration data returned by
+    ZConfig.
+    """
+
+    def __init__(self, section):
+        """Initialize the factory based on a <server> section."""
+        self.type = section.type
+        self.address = section.address
+        self.verbose = section.verbose
+
+    def create(self, task_dispatcher, database):
+        """Return a server based on the server types defined via ZCML."""
+        servertype = getServerType(self.type)
+        # The server object self-registers with the asyncore mainloop.
+        servertype.create(task_dispatcher, database,
+                          self.address[1], # XXX maybe improve API
+                          self.verbose)


=== Zope3/src/zope/app/process/servertype.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/servertype.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# 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, implements
+from zope.app.process.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
+
+
+    def create(self, task_dispatcher, db, port=None, verbose=None):
+        'See 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()})


=== Zope3/src/zope/app/process/servertyperegistry.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/servertyperegistry.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# 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$
+"""
+from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
+from zope.app.process.servertype import IServerType
+from zope.app.process.simpleregistry import SimpleRegistry
+from zope.interface import implements
+
+
+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.
+    """
+
+
+class ServerTypeRegistry(SimpleRegistry):
+    """Registry for the various Server types"""
+    implements(IServerTypeRegistry)
+
+
+ServerTypeRegistry = ServerTypeRegistry(IServerType)
+registerServerType = ServerTypeRegistry.register
+getServerType = ServerTypeRegistry.get


=== Zope3/src/zope/app/process/simpleregistry.py 1.1 => 1.2 ===
--- /dev/null	Wed Jun 25 11:30:04 2003
+++ Zope3/src/zope/app/process/simpleregistry.py	Wed Jun 25 11:29:32 2003
@@ -0,0 +1,86 @@
+##############################################################################
+#
+# 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$
+"""
+from zope.app.interfaces.startup.simpleregistry import ISimpleRegistry
+from types import ListType, TupleType
+from zope.interface import implements
+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 _clear(self):
+        self.objects.clear()
+
+    def register(self, name, object):
+        '''See ISimpleRegistry'''
+
+        if name in self.objects.keys():
+            raise ZopeDuplicateRegistryEntryError(name)
+
+        if self.interface.isImplementedBy(object):
+            self.objects[name] = object
+        else:
+            raise ZopeIllegalInterfaceError(name, self.interface)
+
+        return []
+
+    def get(self, name):
+        '''See ISimpleRegistry'''
+        if name in self.objects.keys():
+            return self.objects[name]
+        else:
+            return None