[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 <= 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 <server>
+ 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