From 49400d403ecbe3050a1a7e4662a2d149668c61b0 Mon Sep 17 00:00:00 2001 From: Kazuhiko Shiozaki <kazuhiko@nexedi.com> Date: Mon, 7 Oct 2013 11:59:24 +0200 Subject: [PATCH] we don't use ClockServer. we use TimerService instead. --- product/ClockServer/ClockServer.py | 108 -------------- product/ClockServer/OriginalClockServer.py | 162 --------------------- product/ClockServer/README | 45 ------ product/ClockServer/__init__.py | 0 product/ClockServer/component.xml | 52 ------- product/ClockServer/datatypes.py | 34 ----- 6 files changed, 401 deletions(-) delete mode 100644 product/ClockServer/ClockServer.py delete mode 100644 product/ClockServer/OriginalClockServer.py delete mode 100644 product/ClockServer/README delete mode 100644 product/ClockServer/__init__.py delete mode 100644 product/ClockServer/component.xml delete mode 100644 product/ClockServer/datatypes.py diff --git a/product/ClockServer/ClockServer.py b/product/ClockServer/ClockServer.py deleted file mode 100644 index b2025b51a5..0000000000 --- a/product/ClockServer/ClockServer.py +++ /dev/null @@ -1,108 +0,0 @@ -############################################################################## -# -# Copyright (c) 2008 Nexedi SARL and Contributors. All Rights Reserved. -# Vincent Pelletier <vincent@nexedi.com> -# -# WARNING: This program as such is intended to be used by professional -# programmers who take the whole responsability of assessing all potential -# consequences resulting from its eventual inadequacies and bugs -# End users who are looking for a ready-to-use solution with commercial -# garantees and support are strongly adviced to contract a Free Software -# Service Company -# -# This program is Free Software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# -############################################################################## - -from OriginalClockServer import ClockServer as OriginalClockServer -from OriginalClockServer import DummyChannel -from ZServer.medusa.http_server import http_request -from ZServer.HTTPResponse import make_response -from ZPublisher.HTTPRequest import HTTPRequest -import StringIO -import thread - -wait_for_close_lock = thread.allocate_lock() - -class ClockServer(OriginalClockServer): - running = True - - def __init__(self, *args, **kw): - self.shutdown_method = kw.pop('shutdown_method') - OriginalClockServer.__init__(self, *args, **kw) - if self.shutdown_method is None: - self.log_info('ClockServer shutdown_method is not set in configuration'\ - 'file. Unclean shutdown can happen.', type='warning') - - def readable(self): - """ - Avoid starting a new tic if shutdown started. - """ - if self.running: - OriginalClockServer.readable(self) - return False - - def clean_shutdown_control(self, _shutdown_phase, time_in_this_phase): - """ - Inform invoked method that a shutdown is in progress. - - Here we: - - Prevent regular tics from being sent. This does not prevent - already-issued tics from running. - - Issue a special tic, ran asynchronously from regular tics and - asynchronously from this thread. - - Wait for that special tic to return, so that we know all clean - shutdown handlers have completely run. - - Return control to caller. - - To wait for shutdown handler to return, it has been chosen to use a - semaphore scheme. It has the following drawbacks: - - It is intrusive: we need to hook foreign classes, since it's not - the way things happen with regular zope data exchange. - - We can't get what the shutdown handler returned (http return code, - page content, ...) so we will never take Lifetime's veto. So shutdown - handler must block until shutdown is complete, which is not how - clean_shutdown_control is supposed to work. Note though that it is a - design weakness in clean_shutdown_control, since some shutdown - handlers might not have finshed their job at the time process gets - closed. - """ - self.running = False - if self.shutdown_method is not None: - # XXX: should use a float for time representation - method = '%s?phase:int=%i&time_in_phase:float=%f' % \ - (self.shutdown_method, _shutdown_phase, time_in_this_phase) - - stdin = StringIO.StringIO() - request_string = 'GET %s HTTP/1.0' % (method, ) - request = http_request(DummyChannel(self), request_string, 'GET', method, - '1.0', self.headers) - environment = self.get_env(request) - response = make_response(request, environment) - # Hook response._finish to get a notification when request is over. - def _finish(): - response.__class__._finish(response) - wait_for_close_lock.release() - response._finish = _finish - # (end of hook) - zope_request = HTTPRequest(stdin, environment, response) - wait_for_close_lock.acquire() - self.zhandler('Zope2', zope_request, response) - self.log_info('ClockServer: Waiting for shutdown handler.') - wait_for_close_lock.acquire() - self.log_info('ClockServer: Going on.') - wait_for_close_lock.release() - return 0 # TODO: detect an error to allow taking the veto. - diff --git a/product/ClockServer/OriginalClockServer.py b/product/ClockServer/OriginalClockServer.py deleted file mode 100644 index fe3a54074c..0000000000 --- a/product/ClockServer/OriginalClockServer.py +++ /dev/null @@ -1,162 +0,0 @@ -############################################################################## -# -# Copyright (c) 2005 Chris McDonough. All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (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 -# -############################################################################## - -""" Zope clock server. Generate a faux HTTP request on a regular basis -by coopting the asyncore API. """ - -import posixpath -import os -import socket -import time -import StringIO -import asyncore - -from ZServer.medusa.http_server import http_request -from ZServer.medusa.default_handler import unquote -from ZServer.PubCore import handle -from ZServer.HTTPResponse import make_response -from ZPublisher.HTTPRequest import HTTPRequest - -def timeslice(period, when=None, t=time.time): - if when is None: - when = t() - return when - (when % period) - -class LogHelper: - def __init__(self, logger): - self.logger = logger - - def log(self, ip, msg, **kw): - self.logger.log(ip + ' ' + msg) - -class DummyChannel: - # we need this minimal do-almost-nothing channel class to appease medusa - addr = ['127.0.0.1'] - closed = 1 - - def __init__(self, server): - self.server = server - - def push_with_producer(self): - pass - - def close_when_done(self): - pass - -class ClockServer(asyncore.dispatcher): - # prototype request environment - _ENV = dict(REQUEST_METHOD = 'GET', - SERVER_PORT = 'Clock', - SERVER_NAME = 'Zope Clock Server', - SERVER_SOFTWARE = 'Zope', - SERVER_PROTOCOL = 'HTTP/1.0', - SCRIPT_NAME = '', - GATEWAY_INTERFACE='CGI/1.1', - REMOTE_ADDR = '0') - - # required by ZServer - SERVER_IDENT = 'Zope Clock' - - def __init__ (self, method, period=60, user=None, password=None, - host=None, logger=None, handler=None): - self.period = period - self.method = method - - self.last_slice = timeslice(period) - - h = self.headers = [] - h.append('User-Agent: Zope Clock Server Client') - h.append('Accept: text/html,text/plain') - if not host: - host = socket.gethostname() - h.append('Host: %s' % host) - auth = False - if user and password: - encoded = ('%s:%s' % (user, password)).encode('base64') - h.append('Authorization: Basic %s' % encoded) - auth = True - - asyncore.dispatcher.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.logger = LogHelper(logger) - self.log_info('Clock server for "%s" started (user: %s, period: %s)' - % (method, auth and user or 'Anonymous', self.period)) - if handler is None: - # for unit testing - handler = handle - self.zhandler = handler - - def get_requests_and_response(self): - out = StringIO.StringIO() - s_req = '%s %s HTTP/%s' % ('GET', self.method, '1.0') - req = http_request(DummyChannel(self), s_req, 'GET', self.method, - '1.0', self.headers) - env = self.get_env(req) - resp = make_response(req, env) - zreq = HTTPRequest(out, env, resp) - return req, zreq, resp - - def get_env(self, req): - env = self._ENV.copy() - (path, params, query, fragment) = req.split_uri() - if params: - path = path + params # undo medusa bug - while path and path[0] == '/': - path = path[1:] - if '%' in path: - path = unquote(path) - if query: - # ZPublisher doesn't want the leading '?' - query = query[1:] - env['PATH_INFO']= '/' + path - env['PATH_TRANSLATED']= posixpath.normpath( - posixpath.join(os.getcwd(), env['PATH_INFO'])) - if query: - env['QUERY_STRING'] = query - env['channel.creation_time']=time.time() - for header in req.header: - key,value = header.split(":",1) - key = key.upper() - value = value.strip() - key = 'HTTP_%s' % ("_".join(key.split( "-"))) - if value: - env[key]=value - return env - - def readable(self): - # generate a request at most once every self.period seconds - slice = timeslice(self.period) - if slice != self.last_slice: - # no need for threadsafety here, as we're only ever in one thread - self.last_slice = slice - req, zreq, resp = self.get_requests_and_response() - self.zhandler('Zope2', zreq, resp) - return False - - def handle_read(self): - return True - - def handle_write (self): - self.log_info('unexpected write event', 'warning') - return True - - def writable(self): - return False - - def handle_error (self): # don't close the socket on error - (file,fun,line), t, v, tbinfo = asyncore.compact_traceback() - self.log_info('Problem in Clock (%s:%s %s)' % (t, v, tbinfo), - 'error') - - - diff --git a/product/ClockServer/README b/product/ClockServer/README deleted file mode 100644 index 171e8b9cbd..0000000000 --- a/product/ClockServer/README +++ /dev/null @@ -1,45 +0,0 @@ -This is an unnoficial Zope 2.8 backport of official Zope's ClockServer. - -Status of initial checkin compared to official version: - __init__.py - Locally created. - ClockServer.py - Locally created. - component.xml - Fix component prefix. - datatypes.py - Import ServerFactory (originaly locally defined). - Fix ClockServer class import. - OriginalClockServer.py - Unchanged original ClockServer.py. - README - Locally created. - -To enable it, add (and adapt) the following to your zope.conf: - %import Products.ClockServer - <clock-server> - # starts a clock which calls /foo/bar every 30 seconds - method /foo/bar - period 30 - user admin - password 123 - </clock-server> - -ERP5 users: You are strongly encouraged to kee TimerService (but to stop using -timerserver) and use the following configuration: - - method /Control_Panel/timer_service/process_timer?interval:int=5 - shutdown_method /Control_Panel/timer_service/process_shutdown - period 5 - -Note: Because ClockServer uses asyncore's "readable" method polling, -configured frequency is only a maximum value. Minimum freqency depends on -asyncore configuration (one wakeup every 30s on my machine). If there is -activity on Zope's sockets, frequency will increase. - -This ClockServer is extended (see ClockServer.py) to propagate shutdown -notification to configured method, by pasing it extra parameters. -This allows method to put shutdown sequence on hold (but not interrupt it). -Also, note that it must not be abused: it's both bad to make user wait, and -there are some timeouts killing Zope if it takes too long to stop. - diff --git a/product/ClockServer/__init__.py b/product/ClockServer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/product/ClockServer/component.xml b/product/ClockServer/component.xml deleted file mode 100644 index 3b3608342a..0000000000 --- a/product/ClockServer/component.xml +++ /dev/null @@ -1,52 +0,0 @@ -<component prefix="Products.ClockServer.datatypes"> - <sectiontype name="clock-server" - datatype=".ClockServerFactory" - implements="ZServer.server"> - <key name="method" datatype="string"> - <description> - The traversal path (from the Zope root) to an - executable Zope method (Python Script, external method, product - method, etc). The method must take no arguments. Ex: "/site/methodname" - </description> - </key> - <key name="shutdown_method" datatype="string"> - <description> - The traversal path (from the Zope root) to an - executable Zope method (Python Script, external method, product - method, etc). The method must take the following arguments: - phase (number) - Shutdown phase number. - See Lifetime.py . - time_in_phase (number) - Time spent in current phase, in seconds. - Given method path must not contain any argument. Ex: "/site/methodname" - </description> - </key> - <key name="period" datatype="integer" default="60"> - <description> - The number of seconds between each clock "tick" (and - thus each call to the above "method"). The lowest number - providable here is typically 30 (this is the asyncore mainloop - "timeout" value). The default is 60. Ex: "30" - </description> - </key> - <key name="user" datatype="string"> - <description> - A zope username. Ex: "admin" - </description> - </key> - <key name="password" datatype="string"> - <description> - The password for the zope username provided above. Careful: this - is obviously not encrypted in the config file. Ex: "123" - </description> - </key> - <key name="host" datatype="string"> - <description> - The hostname passed in via the "Host:" header in the - faux request. Could be useful if you have virtual host rules - set up inside Zope itself. Ex: "www.example.com" - </description> - </key> - </sectiontype> -</component> diff --git a/product/ClockServer/datatypes.py b/product/ClockServer/datatypes.py deleted file mode 100644 index 6a6653cdb5..0000000000 --- a/product/ClockServer/datatypes.py +++ /dev/null @@ -1,34 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (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. -# -############################################################################## - -from ZServer.datatypes import ServerFactory - -class ClockServerFactory(ServerFactory): - def __init__(self, section): - ServerFactory.__init__(self) - self.method = section.method - self.shutdown_method = section.shutdown_method - self.period = section.period - self.user = section.user - self.password = section.password - self.hostheader = section.host - self.host = None # appease configuration machinery - - def create(self): - from Products.ClockServer.ClockServer import ClockServer - from ZServer.AccessLogger import access_logger - return ClockServer(self.method, self.period, self.user, - self.password, self.hostheader, access_logger, - shutdown_method=self.shutdown_method) - -- 2.30.9