view DirectorySession.py @ 25:4a4bd554b4c1 tip

Closing this sub branch.
author Daniele Barchiesi <daniele.barchiesi@eecs.qmul.ac.uk>
date Mon, 25 Mar 2013 14:02:54 +0000
parents 032bc65ebafc
children 89978106f0bb
line wrap: on
line source
"""
DiractorySession.py

Created by  George Fazekas, QMUL on 2009-06-03.
Copyright (c) 2009 QMUL. All rights reserved.

This Cherrypy extension implements a hybrid session type,
where data is held in memory, while files are written to
a directory created for each session.

To use it as a builtin session type, we use:
cherrypy.lib.sessions.DirectorySession = DirectorySession
"""
from __future__ import with_statement
import os,sys,time,shutil,datetime,logging,threading,cherrypy
from cherrypy.lib.sessions import Session
from cherrypy.lib import static

from sawautil import convertFilename

class DirectorySession(Session):

	cache = {}
	locks = {}
	instanceList = []
	
	def __init__(self, id=None, **kwargs):
		# for i in self.instanceList : 
		# 	print 'previous-sessions: ', i.id
		# 	if (i.id == id) :
		# 		print 'got instance already!'
		# self.instanceList.append(self)
		# print 'instance initialised:' + str(len(self.instanceList))
		# kwargs['SESSIONS_PATH'] = os.path.abspath(kwargs['SESSIONS_PATH'])

		if not id in self.cache: 
			new_session_signal = True
		else :
			new_session_signal = False
			
		Session.__init__(self, id=id, **kwargs)

		if new_session_signal or not self.has_key('name'):
			active_sessions = str(self.__len__())
			self.name = "Session %s.%s: " %(active_sessions, str(self.id)[-2:])
			self['name'] = self.name
			self['log_count'] = 0
			try :
				# try to write initial session data into the log file
				log_msg = "%sCherrypy::DirectorySession:New Session Created: ID = %s active sessions: %s \n" %(self.name,str(self.id),active_sessions)
				log_msg += '\n'.join(map ( lambda x: "    %s: %s " %x , cherrypy.request.headers.items() ))
				cherrypy.log(log_msg)
			except :
				print "An error has occured while writing initial session data to the log file."

	def setup(cls,**kwargs):
		print 'Setting up storage class... Nothing to do here yet.'
		for k, v in kwargs.iteritems():
			setattr(cls, k, v)
			print k,v
		# cls.SESSIONS_PATH = kwargs['SESSIONS_PATH'] we could setup a cleanup thread here for the dirs too. kwargs['sessions_path'] = os.path.abspath(kwargs['sessions_path'])
		
		# NOTE: This is now passed in via tools.config: 
		# tools.sessions.SESSIONS_PATH
		
		# DELETION COMMENTED OUT
		#if (os.path.exists(cls.SESSIONS_PATH)) :
		#	shutil.rmtree (cls.SESSIONS_PATH, ignore_errors=True, onerror=None)
		#	cherrypy.log('Cherrypy::DirectorySession:setup (classmethod) Existing session directory removed.')
		if not (os.path.exists(cls.SESSIONS_PATH)) :
			os.mkdir(cls.SESSIONS_PATH)
			cherrypy.log('Cherrypy::DirectorySession:setup (classmethod) New sessions path created: %s' %str(cls.SESSIONS_PATH))
			

	setup = classmethod(setup)
	
	def get_session_name(self):
		""" Return the human readable name of the session. """
		if self.has_key('name') :
			return self['name']
		else :
			return 'Session None: '
			
	def log(self,msg,context='',severity=logging.DEBUG,traceback=True):
		""" Write into cherrypy.log using the session prefix."""
		if not self.has_key('log_count') : self['log_count'] = 0
		log_msg = self.get_session_name() + "m%03i. %s"  %(self['log_count'],msg)
		self['log_count'] += 1
		cherrypy.log(log_msg,context,severity,traceback)
	
	def get_session_path(self):
		""" Return a valid path for this session """
		if self.has_key('session_path') : 
			return self.get('session_path',None)
		else :
			if not self.id : 
				raise AssertionError("Can not create directory for uninitialised session.")
				return None
			sessionPath = os.path.join(self.SESSIONS_PATH,str(self.id))
			if os.path.exists(sessionPath) :
				# clash of session IDs (unprobable), flush data
				shutil.rmtree (sessionPath, ignore_errors=True, onerror=None)
				print "flushed data in %s" % sessionPath
			try : 
				os.makedirs(sessionPath)
			except:
				raise IOError("Session directory for id %r not created." % self.id)
				return None
			if os.path.exists(sessionPath) :
				# create an entry in self so that the clean_up thread knows about this session
				# setattr(self._data,'session_path',sessionPath)
				self['session_path'] = sessionPath
				return  sessionPath

			
	# TODO: the other use case: write the output of a pipe
	def write_file(self,fileObj,fileName,filetype = None):
		pass
		
	def write_data(self,data,filename,filetype = None):
		data = str(data)
		sessionPath = self.get_session_path()
		filePath = os.path.join(sessionPath,filename)
		# print 'writing file to: ',filePath
		with open(filePath,'wb') as f:
			f.writelines(data)

		# check file and save filetype key if given
		if not os.path.exists(filePath) \
		or not os.stat(filePath).st_size > 0 :
			cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession:write_data: Warning: Could not verify file: %s " %filePath)
			return False
		elif not filetype :
			return True
		elif self.has_key(filetype) :
			ol = self.get(filetype,None)
			ol.append(os.path.basename(filePath))
			self[filetype] = list(ol)
		else :
			self[filetype]= list([os.path.basename(filePath)])
		self.save()
		return True


	def write_fsfile(self,fsFile,filetype = None):
		'''Write file to session given as CGI field-storage object.'''
		#fsFile is a cgi.FieldStorage object : cherrypy._cpcgifs.FieldStorage()
		if hasattr (fsFile,'filename') and hasattr(fsFile,'file'):
			return self._write_file(fsFile.file,fsFile.filename,filetype)
			
	def write_fsfile_as(self,fsFile,filename,filetype = None):
		'''Write file to session given as CGI field-storage object.'''
		#fsFile is a cgi.FieldStorage object : cherrypy._cpcgifs.FieldStorage()
		if hasattr (fsFile,'filename') and hasattr(fsFile,'file'):
			return self._write_file(fsFile.file,filename,filetype)
	
	def _write_file(self,file,filename,filetype):
		'''Write file to session given as python file object.'''
		filename = convertFilename(filename)
		sessionPath = self.get_session_path()
		filePath = os.path.join(sessionPath,filename)
		# print 'writing file to: ',filePath

		file.seek(0)
		newFile = open(filePath,'wb')
		try :
			shutil.copyfileobj(file,newFile)
		except :
			try :
				while True:
					data = file.read(8192)
					if not data: break
					newFile.write(data)
			except:
				# newFile.close()
				cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession: Could not write file to session: %i " %self.id)
				raise IOError("Cherrypy::DirectorySession: Could not write file to session %r " %self.id)
		finally:
			newFile.close()
			
		if not os.path.exists(filePath) \
		or not os.stat(filePath).st_size > 0 :
			cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession:_write_file: Warning: Failed to verify file at: %s " %filePath)
			return False
		elif not filetype : 
			return True
		elif self.has_key(filetype) :
			ol = self.get(filetype,None)
			ol.append(os.path.basename(filePath))
			self[filetype] = list(ol)
		else :
			self[filetype]= list([os.path.basename(filePath)])
		self.save()
		return True
			
	def getFiles(self,filelist):
		pass
           
	def clean_up(self):
		"""Clean up expired sessions."""
		now = datetime.datetime.now()
		log_msg = "Session x.xx: mxxx. Cherrypy::DirectorySession: Cleaning up session data at: %s Length of cache = %i " %(str(now), len(self.cache))
		print '\nCeaning up session data at %s: \n===========================' % now
		print 'Length of cache = ', len(self.cache)
		print 'cache members: ', self.cache.keys()
		cherrypy.log(log_msg)
		
		#clean up temp if server not in use
		temp_path='/Users/Shared/george/sawa/sonic-annotator-webapp/static/temp/'
		if len(self.cache.keys()) == 0 and os.path.exists(temp_path):
			try :
				for fname in os.listdir(temp_path):
					os.remove(os.path.join(temp_path,fname))
				print "Note: Cleaned temp directory."
			except:
				print "Warning: could not clear temp directory."
				pass
		
		for id, (data, expiration_time) in self.cache.items():
			print 'Id: ', id
			print 'expiration time: ', expiration_time
			print 'Length of data: ', len(data)
			print 'data members: ', data.keys()
			print 'SESSIONS_PATH: ', self.SESSIONS_PATH

			if expiration_time < now:

				path = os.path.join(self.SESSIONS_PATH,str(id))
				if os.path.exists(path) :
					for fname in os.listdir(path):
						os.remove(os.path.join(path,fname))
					# os.removedirs(path) -> removes sessions too
					os.rmdir(path)					
				try:
					del self.cache[id]
				except KeyError:
					pass
				try:
					del self.locks[id]
				except KeyError:
					pass
	
	def _exists(self):
		return self.id in self.cache
	
	def _load(self):
		return self.cache.get(self.id)

	#this is always called on an on_end_request hook (by cherrypy.session.save)
	def _save(self, expiration_time):
		print 'Saving Session ', self.id
		self.cache[self.id] = (self._data, expiration_time)
	
	def _delete(self):
		del self.cache[self.id]
	
	def acquire_lock(self):
		"""Acquire an exclusive lock on the currently-loaded session data."""
		self.locked = True
		self.locks.setdefault(self.id, threading.RLock()).acquire()
	
	def release_lock(self):
		"""Release the lock on the currently-loaded session data."""
		self.locks[self.id].release()
		self.locked = False
	
	def __len__(self):
		"""Return the number of active sessions."""
		return len(self.cache)

cherrypy.lib.sessions.DirectorySession = DirectorySession