AppServer.py :  » Web-Frameworks » Webware » Webware-1.0.2 » WebKit » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Webware 
Webware » Webware 1.0.2 » WebKit » AppServer.py
#!/usr/bin/env python

"""The AppServer singleton.

The `AppServer` singleton is the controlling object/process/thread.
`AppServer` receives requests and dispatches them to `Application`
(via `Application.dispatchRawRequest`).

There is only one instance of AppServer, `globalAppServer` contains
that instance. Use it like:

    from WebKit.AppServer import globalAppServer

`ThreadedAppServer` completes the implementation, dispatching
these requests to separate threads. `AppServer`, at least in the
abstract, could support different execution models and environments,
but that support is not yet realized (Will it ever be realized?).

The distinction between `AppServer` and `Application` is somewhat
vague -- both are global singletons and both handle dispatching requests.
`AppServer` works on a lower level, handling sockets and threads.

"""

from threading import Thread,Event

from Common import *
from Object import Object
from Application import Application
from ImportManager import ImportManager
from PlugIn import PlugIn
from PidFile import PidFile,ProcessRunning
from ConfigurableForServerSidePath import ConfigurableForServerSidePath
import Profiler

defaultConfig = {
  'PrintConfigAtStartUp': True,
  'Verbose': True,
  'PlugIns': [],
  'PlugInDirs': [],
  'CheckInterval': 100,
  'PidFile': 'appserver.pid',
}

# This actually gets set inside AppServer.__init__
globalAppServer = None


class AppServer(ConfigurableForServerSidePath, Object):
  """The AppServer singleton.

  Purpose and usage are explained in the module docstring.

  """


  ## Init ##

  def __init__(self, path=None):
    """Sets up and starts the `AppServer`.

    `path` is the working directory for the AppServer
    (directory in which AppServer is contained, by default)

    This method loads plugins, creates the Application object,
    and starts the request handling loop.

    """
    self._running = 0
    self._startTime = time.time()

    global globalAppServer
    if globalAppServer:
      raise ProcessRunning('More than one AppServer'
        ' or __init__() invoked more than once.')
    globalAppServer = self

    # Set up the import manager:
    self._imp = ImportManager()

    ConfigurableForServerSidePath.__init__(self)
    Object.__init__(self)
    if path is None:
      path = os.path.dirname(__file__) # os.getcwd()
    self._serverSidePath = os.path.abspath(path)
    self._webKitPath = os.path.abspath(os.path.dirname(__file__))
    self._webwarePath = os.path.dirname(self._webKitPath)

    self.recordPID()

    self._verbose = self.setting('Verbose')
    self._plugIns = []
    self._requestID = 0

    self.checkForInstall()
    self.config() # cache the config
    self.printStartUpMessage()
    sys.setcheckinterval(self.setting('CheckInterval'))
    self._app = self.createApplication()
    self.loadPlugIns()

    # @@ 2003-03 ib: shouldn't this just be in a subclass's __init__?
    if self.isPersistent():
      self._closeEvent = Event()
      self._closeThread = Thread(target=self.closeThread,
        name="CloseThread")
      # self._closeThread.setDaemon(1)
      self._closeThread.start()
    self._running = 1

  def checkForInstall(self):
    """Check whether Webware was installed.

    Exits with an error message if Webware was not installed.
    Called from `__init__`.

    """
    if not os.path.exists(os.path.join(self._webwarePath, 'install.log')):
      sys.stdout = sys.stderr
      print 'ERROR: You have not installed Webware.'
      print 'Please run install.py from inside the Webware directory.'
      print 'For example:'
      print '> cd ..'
      print '> python install.py'
      print
      sys.exit(0)

  def readyForRequests(self):
    """Declare ready for getting requests.

    Should be invoked by subclasses when they are finally ready to
    accept requests. Records some stats and prints a message.

    """
    if Profiler.startTime is None:
      Profiler.startTime = self._startTime
    Profiler.readyTime = time.time()
    Profiler.readyDuration = Profiler.readyTime - Profiler.startTime
    print "Ready (%.2f seconds after launch)." % Profiler.readyDuration
    print
    sys.stdout.flush()
    sys.stderr.flush()

  def closeThread(self):
    """This method is called when the shutdown sequence is initiated."""
    if self.isPersistent():
      self._closeEvent.wait()
    self.shutDown()

  def initiateShutdown(self):
    """Ask the master thread to begin the shutdown."""
    if self.isPersistent():
      self._closeEvent.set()

  def recordPID(self):
    """Save the pid of the AppServer to a file."""
    if self.setting('PidFile') is None:
      self._pidFile = None
      return
    pidpath = self.serverSidePath(self.setting('PidFile'))
    try:
      self._pidFile = PidFile(pidpath)
    except ProcessRunning:
      raise ProcessRunning('The file ' + pidpath + ' exists\n'
        'and contains a process id corresponding to a running process.\n'
        'This indicates that there is an AppServer already running.\n'
        'If this is not the case, delete this file and restart the AppServer.')

  def shutDown(self):
    """Shut down the AppServer.

    Subclasses may override and normally follow this sequence:
      1. set self._running = 1 (request to shut down)
      2. class specific statements for shutting down
      3. Invoke super's shutDown() e.g., `AppServer.shutDown(self)`
      4. set self._running = 0 (server is completely down)

    """
    if self._running:
      print "AppServer is shutting down..."
      sys.stdout.flush()
      self._running = 1
      self._app.shutDown()
      del self._plugIns
      del self._app
      if self._pidFile:
        self._pidFile.remove() # remove the pid file
      if Profiler.profiler:
        # The profile stats will be dumped by Launch.py.
        # You might also considering having a page/servlet
        # that lets you dump the stats on demand.
        print 'AppServer ran for %0.2f seconds.' % (
          time.time() - Profiler.startTime)
      print "AppServer has been shutdown."
      sys.stdout.flush()
      sys.stderr.flush()
      self._running = 0


  ## Configuration ##

  def defaultConfig(self):
    """The default AppServer.config."""
    return defaultConfig # defined on the module level

  def configFilename(self):
    """Return the name of the AppServer configuration file."""
    return self.serverSidePath('Configs/AppServer.config')

  def configReplacementValues(self):
    """Get config values that need to be escaped."""
    # Since these strings may be eval'ed as ordinary strings,
    # we need to use forward slashes instead of backslashes.
    # Note: This is only needed for old style config files.
    # In new style config files, they are note eval'ed, but used
    # directly, so double escaping would be a bad idea here.
    return {
      'WebwarePath': self._webwarePath.replace('\\', '/'),
      'WebKitPath': self._webKitPath.replace('\\', '/'),
      'serverSidePath': self._serverSidePath.replace('\\', '/'),
      }


  ## Network Server ##

  def createApplication(self):
    """Create and return an application object. Invoked by __init__."""
    return Application(server=self)

  def printStartUpMessage(self):
    """Invoked by __init__, prints a little intro."""
    print 'WebKit AppServer', self.version()
    print 'Part of Webware for Python.'
    print 'Copyright 1999-2009 by Chuck Esterbrook. All Rights Reserved.'
    print 'WebKit and Webware are open source.'
    print 'Please visit: http://www.webwareforpython.org'
    print
    print 'Process id is', os.getpid()
    print 'Date/time is', asclocaltime()
    print 'Python is', sys.version.replace(') [', ')\n[')
    print
    if self.setting('PrintConfigAtStartUp'):
      self.printConfig()


  ## Plug-in loading ##

  def plugIns(self):
    """Return a list of the plug-ins loaded by the app server.

    Each plug-in is a Python package.

    """
    return self._plugIns

  def plugIn(self, name, default=NoDefault):
    """ Return the plug-in with the given name. """
    # @@ 2001-04-25 ce: linear search. yuck.
    # Plus we should guarantee plug-in name uniqueness anyway
    for plugin in self._plugIns:
      if plugin.name() == name:
        return plugin
    if default is NoDefault:
      raise KeyError, name
    else:
      return default

  def loadPlugIn(self, path):
    """Load and return the given plug-in.

    May return None if loading was unsuccessful (in which case this method
    prints a message saying so). Used by `loadPlugIns` (note the **s**).

    """
    plugIn = None
    path = self.serverSidePath(path)
    try:
      plugIn = PlugIn(self, path)
      willNotLoadReason = plugIn.load()
      if willNotLoadReason:
        print '    Plug-in %s cannot be loaded because:\n' \
          '    %s' % (path, willNotLoadReason)
        return None
      plugIn.install()
    except Exception:
      print
      print 'Plug-in', path, 'raised exception.'
      raise
    return plugIn

  def loadPlugIns(self):
    """Load all plug-ins.

    A plug-in allows you to extend the functionality of WebKit without
    necessarily having to modify its source. Plug-ins are loaded by
    AppServer at startup time, just before listening for requests.
    See the docs in `WebKit.PlugIn` for more info.

    """
    plugIns = self.setting('PlugIns')
    plugIns = map(lambda path, ssp=self.serverSidePath: ssp(path), plugIns)

    # Scan each directory named in the PlugInDirs list.
    # If those directories contain Python packages (that don't have
    # a "dontload" file) then add them to the plugs in list.
    for plugInDir in self.setting('PlugInDirs'):
      plugInDir = self.serverSidePath(plugInDir)
      fileNames = os.listdir(plugInDir)
      fileNames.sort()
      for filename in fileNames:
        filename = os.path.normpath(os.path.join(plugInDir, filename))
        if (os.path.isdir(filename)
          and os.path.exists(os.path.join(filename, '__init__.py'))
          and os.path.exists(os.path.join(filename, 'Properties.py'))
          and not os.path.exists(os.path.join(filename, 'dontload'))
          and os.path.basename(filename) != 'WebKit'
          and filename not in plugIns):
          plugIns.append(filename)

    print 'Plug-ins list:', ', '.join(plugIns) or 'empty'

    # Now that we have our plug-in list, load them...
    for plugInPath in plugIns:
      plugIn = self.loadPlugIn(plugInPath)
      if plugIn:
        self._plugIns.append(plugIn)
    print


  ## Accessors ##

  def version(self):
    """Return WebKit version."""
    if not hasattr(self, '_webKitVersionString'):
      from MiscUtils.PropertiesObject import PropertiesObject
      props = PropertiesObject(os.path.join(self.webKitPath(), 'Properties.py'))
      self._webKitVersionString = props['versionString']
    return self._webKitVersionString

  def application(self):
    """Return the Application singleton."""
    return self._app

  def startTime(self):
    """Return the time the app server was started.

    The time is given as seconds, like time().

    """
    return self._startTime

  def numRequests(self):
    """Return the number of requests.

    Returns the number of requests received by this app server
    since it was launched.

    """
    return self._requestID

  def isPersistent(self):
    """Check whether the AppServer is persistent.

    When using `OneShot`, the AppServer will exist only for a single
    request, otherwise it will stay around indefinitely.

    """
    raise AbstractError, self.__class__

  def serverSidePath(self, path=None):
    """Return the absolute server-side path of the WebKit app server.

    If the optional path is passed in, then it is joined with the
    server side directory to form a path relative to the app server.

    """
    if path:
      return os.path.normpath(os.path.join(self._serverSidePath, path))
    else:
      return self._serverSidePath

  def webwarePath(self):
    """Return the Webware path."""
    return self._webwarePath

  def webKitPath(self):
    """Return teh WebKit path."""
    return self._webKitPath


## Main ##

def main():
  """Start the Appserver."""
  try:
    server = AppServer()
    print "Ready."
    print
    print "WARNING: There is nothing to do here with the abstract AppServer."
    print "Use one of the adapters such as WebKit.cgi (with ThreadedAppServer)"
    print "or OneShot.cgi"
    server.shutDown()
  except Exception, exc: # Need to kill the sweeper thread somehow
    print "Caught exception:", exc
    print "Exiting AppServer..."
    server.shutDown()
    del server
    sys.exit()

def kill(pid):
  """Kill a process."""
  try:
    from signal import SIGTERM
    os.kill(pid, SIGTERM)
  except Exception:
    if os.name == 'nt':
      import win32api
      handle = win32api.OpenProcess(1, 0, pid)
      win32api.TerminateProcess(handle, 0)
    else:
      raise

def stop(*args, **kw):
  """Stop the AppServer (which may be in a different process)."""
  print "Stopping the AppServer..."
  if kw.has_key('workDir'):
    # app directory
    pidfile = os.path.join(kw['workDir'], "appserver.pid")
  else:
    # pidfile is in WebKit directory
    pidfile = os.path.join(os.path.dirname(__file__), "appserver.pid")
  try:
    pid = int(open(pidfile).read())
  except Exception:
    print "Cannot read process id from pidfile."
  else:
    try:
      kill(pid)
    except Exception:
      from traceback import print_exc
      print_exc(1)
      print "WebKit cannot terminate the running process."

if __name__ == '__main__':
  main()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.