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