Index: Application.py
===================================================================
--- Application.py	(revision 5703)
+++ Application.py	(working copy)
@@ -5,6 +5,9 @@
 
 from Common import *
 from Object import Object
+from FilterMapping import FilterMapping
+from FilterSink import FilterSink
+from FilterChain import FilterChain
 from ConfigurableForServerSidePath import ConfigurableForServerSidePath
 from ExceptionHandler import ExceptionHandler
 from HTTPRequest import HTTPRequest
@@ -43,8 +46,8 @@
 	access through the Transaction object (``transaction.application()``),
 	or you can do::
 
-	    from AppServer import globalAppServer
-	    application = globalAppServer.application()
+		from AppServer import globalAppServer
+		application = globalAppServer.application()
 
 	Settings for Application are taken from ``Configs/Application.config``,
 	and it is used for many global settings, even if they aren't closely tied
@@ -122,6 +125,11 @@
 			except: # otherwise fall back to standard exception
 				self._404Page = None
 
+		# Set up FilterMappings
+		self._filterMappings = []
+		for (pattern, klass) in self.setting('FilterMappings', []):
+			self._filterMappings.append(FilterMapping(pattern, klass))
+
 	def initVersions(self):
 		"""Get and store versions.
 
@@ -242,6 +250,7 @@
 				'Testing': 'Testing',
 				'Docs': 'Docs',
 				},
+			'FilterMappings':	 [],
 			'Debug': {
 				'Sessions': 0,
 				},
@@ -367,7 +376,7 @@
 		# Create a session object with our session ID
 		sess = Session(transaction, sessionID)
 		# Replace the session if it didn't already exist,
-		# otherwise we just throw it away.  setdefault is an atomic
+		# otherwise we just throw it away.	setdefault is an atomic
 		# operation so this guarantees that 2 different
 		# copies of the session with the same ID never get placed into
 		# the session store, even if multiple threads are calling
@@ -467,13 +476,15 @@
 		exceptions, which are then passed on to `handleExceptionInTransaction`.
 
 		"""
-		trans = None
+		trans		= None
+		filterChain = None
 		try:
 			request = self.createRequestForDict(requestDict)
 			trans = Transaction(application=self, request=request)
 			request.setTransaction(trans)
 			response = request.responseClass()(trans, strmOut)
 			trans.setResponse(response)
+		
 			self.runTransaction(trans)
 			trans.response().deliver()
 		except:
@@ -511,16 +522,34 @@
 		assert format == 'CGI'
 		return HTTPRequest(requestDict)
 
+	def createFilterChain(self, trans):
+		"""Create a chain of filters for a given request.
+
+		Most will be specified in Application.config.  The final one, if it
+		is reached, will return control to the Application at
+		runServletInTransaction.
+		"""
+		uri = trans.request().uri()
+		assert uri is not None
+		chain = FilterChain() # Ideally, get one from a cache
+		#chain.addFilter(SessionFilter(self))
+		for fm in self._filterMappings:
+			if fm.matches(uri):
+				chain.addFilter(fm.filter())
+		chain.addFilter(FilterSink(self)) # Could create one and reuse
+		return chain
+
+
 	def runTransaction(self, trans):
 		"""Run transation.
 
 		Executes the transaction, handling HTTPException errors.
-		Finds the servlet (using the root parser, probably
-		`URLParser.ContextParser`, to find the servlet for the
-		transaction, then calling `runTransactionViaServlet`.
 
+		Loads the chain of Filters for this request and executes them.	The
+		final Filter will pass control back to the Application at
+		runServletInTransaction.
+
 		Called by `dispatchRawRequest`.
-
 		"""
 		# @@ gtalvola: I'm guessing this is not the ideal place
 		# @@ to put this code. But, it works.
@@ -534,21 +563,24 @@
 			elif not request_has_cookie_session and not request_has_path_session:
 				self.handleMissingPathSession(trans)
 				return
-		servlet = None
+		chain = self.createFilterChain(trans)
+		trans.setFilterChain(chain)
 		try:
-			servlet = self._rootURLParser.findServletForTransaction(trans)
-			self.runTransactionViaServlet(servlet, trans)
+			try:
+				chain.awake(trans)
+				chain.respond(trans)
+			finally:
+				chain.sleep(trans)
 		except HTTPExceptions.HTTPException, err:
 			err.setTransaction(trans)
 			trans.response().displayError(err)
 		except EndResponse:
 			pass
-		if servlet:
-			# Return the servlet to its pool
-			self.returnServlet(servlet, trans)
 
-	def runTransactionViaServlet(self, servlet, trans):
-		"""Execute the transaction using the servlet.
+	def runServletInTransaction(self, trans):
+		"""Finds the servlet (using the root parser, probably
+		`URLParser.ContextParser`, to find the servlet for the
+		transaction, then executes the transaction using the servlet.
 
 		This is the `awake`/`respond`/`sleep` sequence of calls, or if
 		the servlet supports it, a single `runTransaction` call (which is
@@ -556,22 +588,26 @@
 		`runTransaction` allows the servlet to override the basic call
 		sequence, or catch errors from that sequence.
 
-		Called by `runTransaction`.
-
+		Called by final Filter in chain (FilterSink)
 		"""
-		trans.setServlet(servlet)
-		if hasattr(servlet, 'runTransaction'):
-			servlet.runTransaction(trans)
-		else:
-			# For backward compatibility (Servlet.runTransaction implements
-			# this same sequence of calls, but by keeping it in the servlet
-			# it's easier for the servlet to catch exceptions).
-			try:
-				trans.awake()
-				trans.respond()
-			finally:
-				trans.sleep()
+		try:
+			servlet = self._rootURLParser.findServletForTransaction(trans)
+			trans.setServlet(servlet)
+			if hasattr(servlet, 'runTransaction'):
+				servlet.runTransaction(trans)
+			else:
+				# For backward compatibility (Servlet.runTransaction implements
+				# this same sequence of calls, but by keeping it in the servlet
+				# it's easier for the servlet to catch exceptions).
+				try:
+					trans.awake()
+					trans.respond()
+				finally:
+					trans.sleep()
+		finally:
+			self.returnServlet(servlet, trans)
 
+
 	def forward(self, trans, url):
 		"""Forward the request to a different (internal) URL.
 
