#!/usr/bin/env python from Common import * from UserDict import UserDict from Object import Object from Servlet import Servlet from ServletFactory import * from UnknownFileTypeServlet import UnknownFileTypeServletFactory from types import FloatType from glob import glob import imp import string from threading import Lock, Thread, Event from time import * from fnmatch import fnmatch from WebKit.Cookie import Cookie from WebUtils.HTMLForException import HTMLForException from ExceptionHandler import ExceptionHandler from ConfigurableForServerSidePath import ConfigurableForServerSidePath from TaskKit.Scheduler import Scheduler from ASStreamOut import ASStreamOut debug = 0 class ApplicationError(Exception): pass class EndResponse(Exception): """ Used to prematurely break out of the awake()/respond()/sleep() cycle without reporting a traceback. During servlet processing, if this exception is caught during respond() then sleep() is called and the response is sent. If caught during awake() then both respond() and sleep() are skipped and the response is sent. """ pass class Application(ConfigurableForServerSidePath, Object): """ FUTURE * 2000-04-09 ce: Automatically open in browser. * 2000-04-09 ce: Option to remove HTML comments in responses. * 2000-04-09 ce: Option remove unnecessary white space in responses. * 2000-04-09 ce: Debugging flag and debug print method. * 2000-04-09 ce: A web-based, interactive monitor to the application. * 2000-04-09 ce: Record and playback of requests and responses. Useful for regression testing. * 2000-04-09 ce: sessionTimeout() and a hook for when the session has timed out. * 2000-04-09 ce: pageRefreshOnBacktrack * 2000-04-09 ce: terminate() and isTerminating() * 2000-04-09 ce: isRefusingNewSessions() * 2000-04-09 ce: terminateAfterTimeInterval() * 2000-04-09 ce: restoreSessionWithID:inTransaction: * 2000-04-09 ce: pageWithNameForRequest/Transaction() (?) * 2000-04-09 ce: port() and setPort() (?) * 2000-04-09 ce: Does request handling need to be embodied in a separate object? - Probably, as we may want request handlers for various file types. * 2000-04-09 ce: Concurrent request handling (probably through multi-threading) """ ## Init ## def __init__(self, server=None, transactionClass=None, sessionClass=None, requestClass=None, responseClass=None, exceptionHandlerClass=None, contexts=None, useSessionSweeper=1): self._server = server self._serverSidePath = server.serverSidePath() ConfigurableForServerSidePath.__init__(self) Object.__init__(self) if self.setting('PrintConfigAtStartUp'): self.printConfig() self.initVersions() if transactionClass: self._transactionClass = transactionClass else: from Transaction import Transaction self._transactionClass = Transaction if sessionClass: self._sessionClass = sessionClass else: from Session import Session self._sessionClass = Session if requestClass: self._requestClass = requestClass else: from HTTPRequest import HTTPRequest self._requestClass = HTTPRequest if responseClass: self._responseClass = responseClass else: from HTTPResponse import HTTPResponse self._responseClass = HTTPResponse if exceptionHandlerClass: self._exceptionHandlerClass = exceptionHandlerClass else: self._exceptionHandlerClass = None # Init other attributes self._servletCacheByPath = {} self._serverSideInfoCacheByPath = {} self._cacheDictLock = Lock() self._instanceCacheSize = self._server.setting('MaxServerThreads') self._shutDownHandlers = [] # Set up servlet factories self._factoryList = [] # the list of factories self._factoryByExt = {} # a dictionary that maps all known extensions to their factories, for quick look up self.addServletFactory(PythonServletFactory(self)) self.addServletFactory(UnknownFileTypeServletFactory(self)) # ^ @@ 2000-05-03 ce: make this customizable at least through a method (that can be overridden) if not a config file (or both) ## TaskManager if self._server.isPersistent(): self._taskManager = Scheduler(1) self._taskManager.start() ## End TaskManager ## Contexts if contexts: #Try to get this from the Config file defctxt = contexts else: #Get it from Configurable object, which gets it from defaults or the user config file defctxt = self.setting('Contexts') self._contexts={} # First load all contexts except the default contextDirToName = {} for i in defctxt.keys(): if i != 'default': if not os.path.isabs(defctxt[i]): path = self.serverSidePath(defctxt[i]) else: path = defctxt[i] self.addContext(i, path) contextDirToName[path] = i # @@ gat: this code would be much cleaner if we had a separate DefaultContext config variable. # load in the default context, if any self._defaultContextName = None if defctxt.has_key('default'): if not os.path.isabs(defctxt['default']): path = self.serverSidePath(defctxt['default']) else: path = defctxt['default'] # see if the default context is the same as one of the other contexts self._defaultContextName = contextDirToName.get(path, None) if self._defaultContextName: # the default context is shared with another context self._setContext('default', self.context(self._defaultContextName)) else: # the default context is separate from the other contexts, so add it like any other context self._defaultContextName = 'default' self.addContext('default', path) print ## End Contexts ## Session store # Create the session store from SessionMemoryStore import SessionMemoryStore from SessionFileStore import SessionFileStore from SessionDynamicStore import SessionDynamicStore klass = locals()['Session'+self.setting('SessionStore','File')+'Store'] assert type(klass) is ClassType self._sessions = klass(self) ## End Session store print 'Current directory:', os.getcwd() self.running = 1 if useSessionSweeper: self.startSessionSweeper() self._cacheServletInstances = self.setting("CacheServletInstances",1) print try: # First try the working dir self._404Page = open(os.path.join(self._serverSidePath,"404Text.txt"),"r").read() except: try: # Then try the directory this file is located in self._404Page = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "404Text.txt"),"r").read() except: # Fall back on a simple string self._404Page = """404 Error
File Not Found: %s"""
# @@ ISB 09/02: make this default or get rid of it eventually
if self.setting('ExtraPathInfo', 0):
self.serverSideInfoForRequestOld = self.serverSideInfoForRequest
self.serverSideInfoForRequest = self.serverSideInfoForRequestNewAlgorithm
self._serverSideInfoCacheByPathNew = {}
self._filesToHideRegexes = []
self._filesToServeRegexes = []
from fnmatch import translate as fnTranslate
import re
for pattern in self.setting('FilesToHide'):
self._filesToHideRegexes.append(
re.compile(fnTranslate(pattern)))
for pattern in self.setting('FilesToServe'):
self._filesToServeRegexes.append(
re.compile(fnTranslate(pattern)))
def initVersions(self):
"""
Initialize attributes that store the Webware and WebKit versions
as both tuples and strings. These are stored in the Properties.py
files.
"""
from MiscUtils.PropertiesObject import PropertiesObject
props = PropertiesObject(os.path.join(self.webwarePath(), 'Properties.py'))
self._webwareVersion = props['version']
self._webwareVersionString = props['versionString']
props = PropertiesObject(os.path.join(self.webKitPath(), 'Properties.py'))
self._webKitVersion = props['version']
self._webKitVersionString = props['versionString']
## Task access
def taskManager(self):
return self._taskManager
## Session sweep task
def startSessionSweeper(self):
from Tasks import SessionTask
import time
task = SessionTask.SessionTask(self._sessions)
tm = self.taskManager()
sweepinterval = self.setting('SessionTimeout')*60/10
tm.addPeriodicAction(time.time()+sweepinterval, sweepinterval, task, "SessionSweeper")
print "Session Sweeper started, and will Timeout in: %s" % (sweepinterval)
## Shutdown
def shutDown(self):
"""
Called by AppServer when it is shuting down. The __del__ function of Application probably won't be called due to circular references.
"""
print "Application is Shutting Down"
self.running = 0
if hasattr(self, '_sessSweepThread'):
# We don't always have this, hence the 'if' above
self._closeEvent.set()
self._sessSweepThread.join()
del self._sessSweepThread
self._sessions.storeAllSessions()
if self._server.isPersistent():
self.taskManager().stop()
del self._sessions
del self._factoryByExt
del self._factoryList
del self._server
del self._servletCacheByPath
# Call all registered shutdown handlers
for shutDownHandler in self._shutDownHandlers:
shutDownHandler()
del self._shutDownHandlers
print "Application has been succesfully shutdown."
def addShutDownHandler(self, func):
"""
Adds this function to a list of functions that are called when the application
shuts down.
"""
self._shutDownHandlers.append(func)
## Config ##
def defaultConfig(self):
return {
'PrintConfigAtStartUp': 1,
'DirectoryFile': ['index', 'Main'],
'ExtensionsToIgnore': ['.pyc', '.pyo', '.py~', '.bak', '.tmpl'],
'ExtensionsToServe': None,
'UseCascadingExtensions':1,
'ExtensionCascadeOrder':['.psp','.py','.html',],
'FilesToHide': ['.*', '*~', '*bak', '*.tmpl', '*.pyc', '*.pyo', '*.config'],
'FilesToServe': None,
'LogActivity': 1,
'ActivityLogFilename': 'Logs/Activity.csv',
'ActivityLogColumns': ['request.remoteAddress', 'request.method', 'request.uri', 'response.size', 'servlet.name', 'request.timeStamp', 'transaction.duration', 'transaction.errorOccurred'],
'SessionStore': 'Memory', # can be File or Memory
'SessionTimeout': 60, # minutes
'IgnoreInvalidSession': 1,
'UseAutomaticPathSessions': 0,
# Error handling
'ShowDebugInfoOnErrors': 1,
'IncludeFancyTraceback': 0,
'FancyTracebackContext': 5,
'UserErrorMessage': 'The site is having technical difficulties with this page. An error has been logged, and the problem will be fixed as soon as possible. Sorry!',
'ErrorLogFilename': 'Logs/Errors.csv',
'SaveErrorMessages': 1,
'ErrorMessagesDir': 'ErrorMsgs',
'EmailErrors': 0, # be sure to review the following settings when enabling error e-mails
'ErrorEmailServer': 'mail.-.com',
'ErrorEmailHeaders': { 'From': '-@-.com',
'To': ['-@-.com'],
'Reply-to': '-@-.com',
'content-type': 'text/html',
'Subject': 'Error'
},
'MaxValueLengthInExceptionReport': 500,
'RPCExceptionReturn': 'traceback',
'ReportRPCExceptionsInWebKit': 1,
'Contexts': { 'default': 'Examples',
'Admin': 'Admin',
'Examples': 'Examples',
'Documentation': 'Documentation',
'Testing': 'Testing',
},
'Debug': {
'Sessions': 0,
},
'OldStyleActions': 0,
}
def configFilename(self):
return self.serverSidePath('Configs/Application.config')
def configReplacementValues(self):
return self._server.configReplacementValues()
## Versions ##
def version(self):
"""
Returns the version of the application. This implementation
returns '0.1'. Subclasses should override to return the correct
version number.
"""
## @@ 2000-05-01 ce: Maybe this could be a setting 'AppVersion'
return '0.1'
def webwareVersion(self):
""" Returns the Webware version as a tuple. """
return self._webwareVersion
def webwareVersionString(self):
""" Returns the Webware version as a printable string. """
return self._webwareVersionString
def webKitVersion(self):
""" Returns the WebKit version as a tuple. """
return self._webKitVersion
def webKitVersionString(self):
""" Returns the WebKit version as a printable string. """
return self._webKitVersionString
## Dispatching Requests ##
def dispatchRawRequest(self, newRequestDict, strmOut):
return self.dispatchRequest(self.createRequestForDict(newRequestDict), strmOut)
def dispatchRequest(self, request, strmOut):
""" Creates the transaction, session, response and servlet for the new request which is then dispatched. The transaction is returned. """
assert request is not None
transaction = None
if request.value('_captureOut_', 0):
real_stdout = sys.stdout
sys.stdout = StringIO()
transaction = self.createTransactionForRequest(request)
response = self.createResponseInTransaction(transaction, strmOut)
try:
ssPath = request.serverSidePath()
if ssPath is None or not os.path.exists(ssPath):
self.handleBadURL(transaction)
elif isdir(ssPath) and noslash(request.pathInfo()): # (*) see below
self.handleDeficientDirectoryURL(transaction)
elif self.isSessionIdProblematic(request):
self.handleInvalidSession(transaction)
elif self.setting('UseAutomaticPathSessions') and not request.hasPathSession():
self.handleMissingPathSession(transaction)
else:
validFile = 1
baseName = os.path.split(ssPath)[1]
for patternToHide in self.setting('FilesToHide'):
if fnmatch(baseName, patternToHide):
validFile = 0
patternsToServe = self.setting('FilesToServe')
if patternsToServe:
validFile = 0
for patternToServe in self.setting('FilesToServe'):
if fnmatch(baseName, patternToServe):
validFile = 1
if not validFile:
self.handleBadURL(transaction)
else:
self.handleGoodURL(transaction)
if request.value('_captureOut_', 0):
response.write('''
%s |
404 Not found: %s' % transaction.request().uri()) res.write(self._404Page % (transaction.request().uri())) # @@ 2000-06-26 ce: This error page is pretty primitive # @@ 2000-06-26 ce: We should probably load a separate template file and display that def handleDeficientDirectoryURL(self, transaction): # @@ 2000-11-29 gat: # This splitting and rejoining is necessary in order to handle # url's like http://localhost/WebKit.cgi/Examples?foo=1 # without infinite looping. I'm not sure this is the "right" # way to do this, as it seems to contradict the docstring of # uri(), but it works. Needs further investigation. uri = string.split(transaction.request().uri(), '?') uriEnd = string.split(uri[0], '/')[-1] # @@ gat 2000-05-19: this was changed to use a relative redirect starting with "." to force # a client redirect instead of a server redirect. This fixes problems on IIS. uri[0] = './' + uriEnd + '/' newURL = string.join(uri, '?') if debug: print "* handleDeficientDirectoryURL - reditrect to",newURL res = transaction.response() res.setHeader('Status', '301 Redirect') res.setHeader('Location', newURL) res.write('''
The document has moved to %s. ''' % (newURL, newURL)) def isSessionIdProblematic(self, request, debug=0): """ Returns 1 if there is a session id and it's not valid (either because it doesn't exist or because it has expired due to inactivity). Having no session id is not considered problematic. This method will also expire the session if it's too old. This method is invoked by dispatchRequest() as one of the major steps in handling requests. """ debug = self.setting('Debug')['Sessions'] if debug: prefix = '>> [session] isSessionIdProblematic:' sid = request.sessionId() if sid: if self._sessions.has_key(sid): if (time()-request.session().lastAccessTime()) >= request.session().timeout(): if debug: print prefix, 'session expired: %s' % repr(sid) del self._sessions[sid] problematic = 1 else: problematic = 0 else: if debug: print prefix, 'session does not exist: %s' % repr(sid) problematic = 1 else: problematic = 0 if debug: print prefix, 'isSessionIdProblematic =', problematic, ', id =', sid return problematic def handleInvalidSession(self, transaction): res = transaction.response() debug = self.setting('Debug')['Sessions'] if debug: prefix = '>> handleInvalidSession:' cookie = Cookie('_SID_', '') cookie.setPath('/') res.addCookie(cookie) if debug: print prefix, "set _SID_ to ''" if self.setting('IgnoreInvalidSession'): # Delete the session ID cookie (and field since session IDs can also # be encoded into fields) from the request, then handle the servlet # as though there was no session try: del transaction.request().cookies()['_SID_'] except KeyError: pass try: transaction.request().delField('_SID_') except KeyError: pass transaction.request().setSessionExpired(1) if self.setting('UseAutomaticPathSessions'): self.handleMissingPathSession(transaction) else: self.handleGoodURL(transaction) else: res.write('''
Your session has expired and all information related to your previous working session with this site has been cleared.
You may try this URL again by choosing Refresh/Reload, or revisit the front page.
''')
# @@ 2000-08-10 ce: This is a little cheesy. We could load a template...
def handleMissingPathSession(self,transaction):
"""
if UseAutomaticPathSessions is enabled in Application.config
we redirect the browser to a url with SID in path
http://gandalf/a/_SID_=2001080221301877755/Examples/
_SID_ is extracted and removed from path in HTTPRequest.py
this is for convinient building of webapps that must not
depend on cookie support
"""
newSid = transaction.session().identifier()
request = transaction.request()
url = request.adapterName() + '/_SID_='+ newSid + '/' + request.pathInfo() + (request.extraURLPath() or '')
if request.queryString():
url = url + '?' + request.queryString()
if self.setting('Debug')['Sessions']:
print ">> [sessions] handling UseAutomaticPathSessions, redirecting to", url
transaction.response().sendRedirect(url)
def handleGoodURL(self, transaction):
self.createServletInTransaction(transaction)
try:
self.awake(transaction)
try:
self.respond(transaction)
except EndResponse:
pass
self.sleep(transaction)
except EndResponse:
pass
def processURLPath(self, req, URL):
"""
Return a URL Path relative to the current request and context.
Absolute references in the URL (starting with '/' are treated
absolute to the current context.
"""
# Construct the url path for the servlet we're calling
urlPath = req.urlPath()
if urlPath=='':
urlPath = '/'
elif urlPath[-1]=='/':
urlPath = urlPath
else:
lastSlash = string.rfind(urlPath, '/')
urlPath = urlPath[:lastSlash+1]
extraPath = ''
if URL[:1] == "/":
extraPath = req.siteRootFromCurrentServlet()
urlPath = WebUtils.Funcs.normURL(urlPath + extraPath + URL)
if debug:
print "*processURLPath(%s)=%s" % (URL, urlPath)
return urlPath
def forward(self, trans, URL):
"""
Enable a servlet to pass a request to another servlet. The Request object is kept the same, and may be used
to pass information to the next servlet. The next servlet may access the parent servlet through request.parent(),
which will return the parent servlet. The first servlet will not be able to send any new response data once
the call to forwardRequest returns.
New Response and Transaction objects are created.
Currently the URL is always relative to the existing URL.
NOTE: @@ sgd 2003-01-15 - presently this goes through dispatchRequest() which
under some circumstances can result in sending a redirect() which causes the
browser to re-get the URL. This defeats the purpose of passing information
to a servlet in the request or transaction objects. This only happens in
cases like a forward to a directory where no trailing / was specified.
"""
# @@ sgd 2003-01-15
# to fix the above warning about using dispatchRequest() consider
# using the includeURL() code but handle the session and clearing
# the output stream here.
if debug: print "> forward(%s)" % str(URL)
req = trans.request()
urlPath = self.processURLPath(req, URL)
#save the original URL
oldURL = req.urlPath()
req.setURLPath(urlPath)
#add a reference to the parent servlet
req.addParent(req.transaction()._servlet)
# Store the session so that the new servlet can access its values
if trans.hasSession():
self._sessions.storeSession(trans.session())
# We might have created a brand-new session prior to this call. If so, we need
# to set the _SID_ identifier in the request so that the new transaction will
# know about the new session.
# gat 200-06-21: this feels like a hack, but it is necessary to prevent losing
# session information.
if trans.hasSession() and not req.hasValue('_SID_'):
if debug: print 'Application.forward(): propagating new session ID into request'
req.setField('_SID_', trans.session().identifier())
#get the output stream and set it in the new response
strmOut = req.transaction().response().streamOut()
strmOut.clear()
newTrans = self.dispatchRequest(req, strmOut)
req.popParent()
req.setURLPath(oldURL)
#give the old response a dummy streamout- nasty hack, better idea anyone?
trans.response()._strmOut = ASStreamOut()
req._transaction = trans #this is needed by dispatchRequest
# Get rid of the session in the old transaction so it won't try to save it,
# thereby wiping out session changes made in the servlet we forwarded to
trans.setSession(None)
def forwardRequest(self, trans, URL):
print "forwardRequest is deprecated. Use forward()"
return self.forward(trans, URL)
def includeURL(self, trans, URL):
"""
Enable a servlet to pass a request to another servlet. This implementation
handles chaining and requestDispatch in Java.
The Request, Rssponse and Session objects are all kept the same, so the Servlet
that is called may receive information through those objects. The catch is that
the function WILL return to the calling servlet, so the calling servlet should either
take advantage of that or return immediately.
Also, if the response has already been partially sent, it can't be reversed.
"""
if debug: print "> includeURL(%s)" % str(URL)
req = trans.request()
#Save the things we're gonna change.
currentPath=req.urlPath()
currentServlet=trans._servlet
urlPath = self.processURLPath(req, URL)
req.setURLPath(urlPath)
req.addParent(currentServlet)
#Get the new servlet
self.createServletInTransaction(trans)
#call the servlet, but not session, it's already alive
try:
trans.servlet().awake(trans)
try:
trans.servlet().respond(trans)
except EndResponse:
pass
trans.servlet().sleep(trans)
except EndResponse:
pass
self.returnInstance(trans,trans.request().serverSidePath())
#replace things like they were
#trans.request()._serverSidePath=currentPath
req.setURLPath(currentPath)
req.popParent()
trans._servlet=currentServlet
def forwardRequestFast(self, trans, url):
print "forwardRequestFast is deprecated. Use includeURL()"
return self.includeURL(trans, url)
def callMethodOfServlet(self, trans, URL, method, *args, **kwargs):
"""
Enable a servlet to call a method of another servlet. Note: the servlet's awake() is called,
then the method is called with the given arguments, then sleep() is called. The result
of the method call is returned.
"""
req = trans.request()
if debug: print "> callMethodOfServlet(%s, %s)" % (URL, method)
# Save the current url path and servlet
currentPath = req.urlPath()
currentServlet = trans._servlet
urlPath = self.processURLPath( req, URL )
# Modify the request to use the new URL path
req.setURLPath(urlPath)
# Add the current servlet as a parent
req.addParent(currentServlet)
# Get the new servlet
self.createServletInTransaction(trans)
# Awaken, call the method, and sleep
servlet = trans.servlet()
try:
servlet.awake(trans)
try:
result = getattr(servlet, method)(*args, **kwargs)
except EndResponse:
pass
servlet.sleep(trans)
except EndResponse:
pass
# Return the servlet instance to the cache
self.returnInstance(trans, trans.request().serverSidePath())
# Replace things like they were
req.setURLPath(currentPath)
req.popParent()
trans._servlet=currentServlet
# Done
return result
## Transactions ##
def awake(self, transaction):
transaction.awake()
def respond(self, transaction):
transaction.respond()
def sleep(self, transaction):
transaction.sleep()
# Store the session
if transaction.hasSession():
self._sessions.storeSession(transaction.session())
## Sessions ##
def session(self, sessionId, default=NoDefault):
if default is NoDefault:
return self._sessions[sessionId]
else:
return self._sessions.get(sessionId, default)
def hasSession(self, sessionId):
return self._sessions.has_key(sessionId)
def sessions(self):
return self._sessions
## Misc Access ##
def server(self):
return self._server
def serverSidePath(self, path=None):
""" Returns the absolute server-side path of the WebKit application. 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 self._server.webwarePath()
def webKitPath(self):
return self._server.webKitPath()
def name(self):
return sys.argv[0]
def transactionClass(self):
return self._transactionClass
def setTransactionClass(self, newClass):
assert isclass(newClass)
self._transactionClass = newClass
def responseClass(self, newClass):
return self._responseClass
def setResponseClass(self, newClass):
assert isclass(newClass)
self._responseClass = newClass
## Contexts ##
def context(self, name, default=NoDefault):
""" Returns the value of the specified context. """
if default is NoDefault:
return self._contexts[name]
else:
return self._contexts.get(name, default)
def hasContext(self, name):
return self._contexts.has_key(name)
def _setContext(self, name, value):#use addContext
if self._contexts.has_key(name):
print 'WARNING: Overwriting context %s (=%s) with %s' % (
repr(name), repr(self._contexts[name]), repr(value))
self._contexts[name] = value
def contexts(self):
return self._contexts
def addContext(self, name, dir):
# Code added by Jose
# __init__ check file code
# this code will check for the __init__ file and add it if necessary
if not os.path.exists(os.path.join(dir, '__init__.py')):
# __init__.py file is missing creat it
print '__init__ file is missing, creating __init__ file now'
init = file(os.path.join(dir, '__init__.py'), 'w')
init.write('# Auto generated __init__ file\n')
init.close()
# end __init__ file check code
# end Code added by Jose
if self._contexts.has_key(name):
print 'WARNING: Overwriting context %s (=%s) with %s' % (
repr(name), repr(self._contexts[name]), repr(dir))
__contextInitialized = 1 # Assume already initialized.
else:
__contextInitialized = 0
try:
importAsName = name
# code added by Jose
if dir.startswith('//'):
localdir, pkgname = os.path.splitunc(dir)
pkgname = pkgname[1:]
else:
localdir, pkgname = os.path.split(dir)
if sys.modules.has_key(importAsName):
mod = sys.modules.get(importAsName)
else:
res = imp.find_module(pkgname, [localdir])
mod = imp.load_module(name, res[0], res[1], res[2])
__contextInitialized = 0 # overwriting context - re-initialize
except ImportError,e:
print "Error loading context: %s: %s: dir=%s" % (name, e, dir)
return
if not __contextInitialized and mod.__dict__.has_key('contextInitialize'):
result = mod.__dict__['contextInitialize'](self,
os.path.normpath(os.path.join(os.getcwd(),dir)))
if result != None and result.has_key('ContentLocation'):
dir = result['ContentLocation']
print 'Loading context: %s at %s' % (name, dir)
self._contexts[name] = dir
## Factory access ##
def addServletFactory(self, factory):
assert isinstance(factory, ServletFactory)
self._factoryList.append(factory)
for ext in factory.extensions():
assert not self._factoryByExt.has_key(ext), 'Extension (%s) for factory (%s) was already used by factory (%s)' % (ext, self._factoryByExt[ext].name(), factory.name())
self._factoryByExt[ext] = factory
def factories(self):
return self._factoryList
## Activity Log ##
def writeActivityLog(self, transaction):
"""
Writes an entry to the script log file. Uses settings ActivityLogFilename and ActivityLogColumns.
"""
filename = self.serverSidePath(self.setting('ActivityLogFilename'))
if os.path.exists(filename):
file = open(filename, 'a')
else:
file = open(filename, 'w')
file.write(string.join(self.setting('ActivityLogColumns'), ',')+'\n')
values = []
# We use UserDict on the next line because we know it inherits NamedValueAccess and reponds to valueForName()
objects = UserDict({
'application': self,
'transaction': transaction,
'request': transaction.request(),
'response': transaction.response(),
'servlet': transaction.servlet(),
'session': transaction._session, #don't cause creation of session
})
for column in self.setting('ActivityLogColumns'):
try:
value = objects.valueForName(column)
except:
value = '(unknown)'
if type(value) is FloatType:
value = '%0.2f' % value # probably need more flexibility in the future
else:
value = str(value)
values.append(value)
file.write(string.join(values, ',')+'\n')
file.close()
for i in objects.keys():
objects[i]=None
## Utilities/Hooks ##
def createRequestForDict(self, newRequestDict):
return self._requestClass(dict=newRequestDict)
def createTransactionForRequest(self, request):
trans = self._transactionClass(application=self, request=request)
request.setTransaction(trans)
return trans
def createResponseInTransaction(self, transaction, strmOut):
response = self._responseClass(transaction, strmOut)
transaction.setResponse(response)
return response
def createSessionForTransaction(self, transaction):
debug = self.setting('Debug')['Sessions']
if debug: prefix = '>> [session] createSessionForTransaction:'
sessId = transaction.request().sessionId()
if debug: print prefix, 'sessId =', sessId
if sessId:
session = self.session(sessId)
if debug: print prefix, 'retrieved session =', session
else:
session = self._sessionClass(transaction)
self._sessions[session.identifier()] = session
if debug: print prefix, 'created session =', session
transaction.setSession(session)
return session
def getServlet(self, transaction, path, cache=None): #send the cache if you want the cache info set
ext = os.path.splitext(path)[1]
# Add the path to sys.path. @@ 2000-05-09 ce: not the most ideal solution, but works for now
dir = os.path.dirname(path)
factory = self._factoryByExt.get(ext, None)
if not factory:
factory = self._factoryByExt.get('.*', None) # special case: .* is the catch-all
if not factory:
raise ApplicationError, 'Unknown extension (%s). No factory found.' % ext
# ^ @@ 2000-05-03 ce: Maybe the web browser doesn't want an exception for bad extensions. We probably need a nicer message to the user...
# On the other hand, that can always be done by providing a factory for '.*'
assert factory.uniqueness()=='file', '%s uniqueness is not supported.' % factory.uniqueness()
# @@ 2001-05-10 gat: removed this because it allows 2 different copies of the same
# module to be imported, one as "foo" and one as "context.foo".
#if not dir in sys.path:
# sys.path.insert(0, dir)
inst = factory.servletForTransaction(transaction)
assert inst is not None, 'Factory (%s) failed to create a servlet upon request.' % factory.name()
if cache:
cache['threadsafe']=inst.canBeThreaded()
cache['reuseable']=inst.canBeReused()
return inst
def returnInstance(self, transaction, path):
""" The only case I care about now is threadsafe=0 and reuseable=1"""
cache = self._servletCacheByPath.get(path, None)
if cache and cache['reuseable'] and not cache['threadsafe']:
srv = transaction.servlet()
if srv:
cache['instances'].append(transaction.servlet())
return
def newServletCacheItem(self,key,item):
""" Safely add new item to the main cache. Not worried about the retrieval for now.
I'm not even sure this is necessary, as it's a one bytecode op, but it doesn't cost
much of anything speed wise.
"""
#self._cacheDictLock.acquire()
self._servletCacheByPath[key] = item
#self._cacheDictLock.release()
def flushServletCache(self):
self._servletCacheByPath = {}
def createServletInTransaction(self, transaction):
# Get the path
path = transaction.request().serverSidePath()
assert path is not None
inst = None
cache = None
# Cached?
if self._cacheServletInstances:
cache = self._servletCacheByPath.get(path, None)
# File is not newer?
if cache and cache['timestamp']