# Copyright (c) 2004 DoCoMo Euro-Labs GmbH (Munich, Germany).
# Copyright (c) 2001-2004 LOGILAB S.A. (Paris, FRANCE).
#
# http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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.
"""some base classes for narval'services


:version: $Revision:$  
:author: Logilab

:copyright:
  2001-2004 LOGILAB S.A. (Paris, FRANCE)
  
  2004 DoCoMo Euro-Labs GmbH (Munich, Germany)
  
:contact:
  http://www.logilab.fr/ -- mailto:contact@logilab.fr
  
  http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com
"""

__revision__ = "$Id: BaseService.py,v 1.8 2001/11/06 14:39:46 alf Exp $"
__docformat__ = "restructuredtext en"

import threading
import select
import socket
from SocketServer import TCPServer

from narval.engine_interfaces import IService


class BaseService:
    """Base service as an abstract class. Concrete class should at least
    override the *_run* method

    :type thread: threading.Thread
    :ivar thread: the thread running the service

    :type name: str
    :ivar name: the name of the service
    
    :type loop: bool
    :ivar loop: flag indicating whether the service is running
    """
    __implements__ = IService
    
    def __init__(self, name='BaseService'):
        self.thread = None
        self.name = name
        self.loop = 0

    def start(self):
        """start the service"""
        self.thread = threading.Thread(name=self.name, target=self._run)
        self.loop = 1
        self.thread.start()
        log(LOG_NOTICE, 'service %s started', self.name)

    def stop(self):
        """stop the service"""
        self.loop = 0
        self.thread.join()
        log(LOG_NOTICE, 'service %s stoped', self.name)
        
    def _run(self):
        """infinite loop used as the service's thread target

        should be overriden in concrete classes
        """
        raise NotImplementedError()


class BaseSocketService(BaseService, TCPServer):
    """base socket service, using SocketServer.TCPServer

    :type allow_reuse_address: bool
    :cvar allow_reuse_address:
      flag indicating whether the socket should have the SO_REUSE_ADDR option
    """

    allow_reuse_address = 1

    def __init__(self, port, handler, name='BaseSocketService') :
        BaseService.__init__(self,  name)
        host = socket.gethostname()
        TCPServer.__init__(self, (host, port), handler)
        log(LOG_NOTICE, 'service %s listening on %s:%d', (name, host, port))
        
    def _run(self) :
        """infinite loop used as the service's thread target

        wait for network data from the socket, using select
        """
        sock = self.socket
        while self.loop :
            try:
                r = select.select([sock], [], [sock], 1)[0]
            except Exception, ex:
                log(LOG_ERR, 'select error: %s', ex)
            else:
                if r:
                    request, client_address = sock.accept()
                    try:
                        self.process_request(request, client_address)
                    except:
                        self.handle_error(request, client_address)
