gyorgy@0
|
1 """
|
gyorgy@0
|
2 DiractorySession.py
|
gyorgy@0
|
3
|
gyorgy@0
|
4 Created by George Fazekas, QMUL on 2009-06-03.
|
gyorgy@0
|
5 Copyright (c) 2009 QMUL. All rights reserved.
|
gyorgy@0
|
6
|
gyorgy@0
|
7 This Cherrypy extension implements a hybrid session type,
|
gyorgy@0
|
8 where data is held in memory, while files are written to
|
gyorgy@0
|
9 a directory created for each session.
|
gyorgy@0
|
10
|
gyorgy@0
|
11 To use it as a builtin session type, we use:
|
gyorgy@0
|
12 cherrypy.lib.sessions.DirectorySession = DirectorySession
|
gyorgy@0
|
13 """
|
gyorgy@0
|
14 from __future__ import with_statement
|
gyorgy@0
|
15 import os,sys,time,shutil,datetime,logging,threading,cherrypy
|
gyorgy@0
|
16 from cherrypy.lib.sessions import Session
|
gyorgy@0
|
17 from cherrypy.lib import static
|
gyorgy@0
|
18
|
gyorgy@0
|
19 from sawautil import convertFilename
|
gyorgy@0
|
20
|
gyorgy@0
|
21 class DirectorySession(Session):
|
gyorgy@0
|
22
|
gyorgy@0
|
23 cache = {}
|
gyorgy@0
|
24 locks = {}
|
gyorgy@0
|
25 instanceList = []
|
gyorgy@0
|
26
|
gyorgy@0
|
27 def __init__(self, id=None, **kwargs):
|
gyorgy@0
|
28 # for i in self.instanceList :
|
gyorgy@0
|
29 # print 'previous-sessions: ', i.id
|
gyorgy@0
|
30 # if (i.id == id) :
|
gyorgy@0
|
31 # print 'got instance already!'
|
gyorgy@0
|
32 # self.instanceList.append(self)
|
gyorgy@0
|
33 # print 'instance initialised:' + str(len(self.instanceList))
|
gyorgy@0
|
34 # kwargs['SESSIONS_PATH'] = os.path.abspath(kwargs['SESSIONS_PATH'])
|
gyorgy@0
|
35
|
gyorgy@0
|
36 if not id in self.cache:
|
gyorgy@0
|
37 new_session_signal = True
|
gyorgy@0
|
38 else :
|
gyorgy@0
|
39 new_session_signal = False
|
gyorgy@0
|
40
|
gyorgy@0
|
41 Session.__init__(self, id=id, **kwargs)
|
gyorgy@0
|
42
|
gyorgy@0
|
43 if new_session_signal or not self.has_key('name'):
|
gyorgy@0
|
44 active_sessions = str(self.__len__())
|
gyorgy@0
|
45 self.name = "Session %s.%s: " %(active_sessions, str(self.id)[-2:])
|
gyorgy@0
|
46 self['name'] = self.name
|
gyorgy@0
|
47 self['log_count'] = 0
|
gyorgy@0
|
48 try :
|
gyorgy@0
|
49 # try to write initial session data into the log file
|
gyorgy@0
|
50 log_msg = "%sCherrypy::DirectorySession:New Session Created: ID = %s active sessions: %s \n" %(self.name,str(self.id),active_sessions)
|
gyorgy@0
|
51 log_msg += '\n'.join(map ( lambda x: " %s: %s " %x , cherrypy.request.headers.items() ))
|
gyorgy@0
|
52 cherrypy.log(log_msg)
|
gyorgy@0
|
53 except :
|
gyorgy@0
|
54 print "An error has occured while writing initial session data to the log file."
|
gyorgy@0
|
55
|
gyorgy@0
|
56 def setup(cls,**kwargs):
|
gyorgy@0
|
57 print 'Setting up storage class... Nothing to do here yet.'
|
gyorgy@0
|
58 for k, v in kwargs.iteritems():
|
gyorgy@0
|
59 setattr(cls, k, v)
|
gyorgy@0
|
60 print k,v
|
gyorgy@0
|
61 # 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'])
|
gyorgy@0
|
62
|
gyorgy@0
|
63 # NOTE: This is now passed in via tools.config:
|
gyorgy@0
|
64 # tools.sessions.SESSIONS_PATH
|
gyorgy@0
|
65
|
gyorgy@0
|
66 # DELETION COMMENTED OUT
|
gyorgy@0
|
67 #if (os.path.exists(cls.SESSIONS_PATH)) :
|
gyorgy@0
|
68 # shutil.rmtree (cls.SESSIONS_PATH, ignore_errors=True, onerror=None)
|
gyorgy@0
|
69 # cherrypy.log('Cherrypy::DirectorySession:setup (classmethod) Existing session directory removed.')
|
gyorgy@0
|
70 if not (os.path.exists(cls.SESSIONS_PATH)) :
|
gyorgy@0
|
71 os.mkdir(cls.SESSIONS_PATH)
|
gyorgy@0
|
72 cherrypy.log('Cherrypy::DirectorySession:setup (classmethod) New sessions path created: %s' %str(cls.SESSIONS_PATH))
|
gyorgy@0
|
73
|
gyorgy@0
|
74
|
gyorgy@0
|
75 setup = classmethod(setup)
|
gyorgy@0
|
76
|
gyorgy@0
|
77 def get_session_name(self):
|
gyorgy@0
|
78 """ Return the human readable name of the session. """
|
gyorgy@0
|
79 if self.has_key('name') :
|
gyorgy@0
|
80 return self['name']
|
gyorgy@0
|
81 else :
|
gyorgy@0
|
82 return 'Session None: '
|
gyorgy@0
|
83
|
gyorgy@0
|
84 def log(self,msg,context='',severity=logging.DEBUG,traceback=True):
|
gyorgy@0
|
85 """ Write into cherrypy.log using the session prefix."""
|
gyorgy@0
|
86 if not self.has_key('log_count') : self['log_count'] = 0
|
gyorgy@0
|
87 log_msg = self.get_session_name() + "m%03i. %s" %(self['log_count'],msg)
|
gyorgy@0
|
88 self['log_count'] += 1
|
gyorgy@0
|
89 cherrypy.log(log_msg,context,severity,traceback)
|
gyorgy@0
|
90
|
gyorgy@0
|
91 def get_session_path(self):
|
gyorgy@0
|
92 """ Return a valid path for this session """
|
gyorgy@0
|
93 if self.has_key('session_path') :
|
gyorgy@0
|
94 return self.get('session_path',None)
|
gyorgy@0
|
95 else :
|
gyorgy@0
|
96 if not self.id :
|
gyorgy@0
|
97 raise AssertionError("Can not create directory for uninitialised session.")
|
gyorgy@0
|
98 return None
|
gyorgy@0
|
99 sessionPath = os.path.join(self.SESSIONS_PATH,str(self.id))
|
gyorgy@0
|
100 if os.path.exists(sessionPath) :
|
gyorgy@0
|
101 # clash of session IDs (unprobable), flush data
|
gyorgy@0
|
102 shutil.rmtree (sessionPath, ignore_errors=True, onerror=None)
|
gyorgy@0
|
103 print "flushed data in %s" % sessionPath
|
gyorgy@0
|
104 try :
|
gyorgy@0
|
105 os.makedirs(sessionPath)
|
gyorgy@0
|
106 except:
|
gyorgy@0
|
107 raise IOError("Session directory for id %r not created." % self.id)
|
gyorgy@0
|
108 return None
|
gyorgy@0
|
109 if os.path.exists(sessionPath) :
|
gyorgy@0
|
110 # create an entry in self so that the clean_up thread knows about this session
|
gyorgy@0
|
111 # setattr(self._data,'session_path',sessionPath)
|
gyorgy@0
|
112 self['session_path'] = sessionPath
|
gyorgy@0
|
113 return sessionPath
|
gyorgy@0
|
114
|
gyorgy@0
|
115
|
gyorgy@0
|
116 # TODO: the other use case: write the output of a pipe
|
gyorgy@0
|
117 def write_file(self,fileObj,fileName,filetype = None):
|
gyorgy@0
|
118 pass
|
gyorgy@0
|
119
|
gyorgy@0
|
120 def write_data(self,data,filename,filetype = None):
|
gyorgy@0
|
121 data = str(data)
|
gyorgy@0
|
122 sessionPath = self.get_session_path()
|
gyorgy@0
|
123 filePath = os.path.join(sessionPath,filename)
|
gyorgy@0
|
124 # print 'writing file to: ',filePath
|
gyorgy@0
|
125 with open(filePath,'wb') as f:
|
gyorgy@0
|
126 f.writelines(data)
|
gyorgy@0
|
127
|
gyorgy@0
|
128 # check file and save filetype key if given
|
gyorgy@0
|
129 if not os.path.exists(filePath) \
|
gyorgy@0
|
130 or not os.stat(filePath).st_size > 0 :
|
gyorgy@0
|
131 cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession:write_data: Warning: Could not verify file: %s " %filePath)
|
gyorgy@0
|
132 return False
|
gyorgy@0
|
133 elif not filetype :
|
gyorgy@0
|
134 return True
|
gyorgy@0
|
135 elif self.has_key(filetype) :
|
gyorgy@0
|
136 ol = self.get(filetype,None)
|
gyorgy@0
|
137 ol.append(os.path.basename(filePath))
|
gyorgy@0
|
138 self[filetype] = list(ol)
|
gyorgy@0
|
139 else :
|
gyorgy@0
|
140 self[filetype]= list([os.path.basename(filePath)])
|
gyorgy@0
|
141 self.save()
|
gyorgy@0
|
142 return True
|
gyorgy@0
|
143
|
gyorgy@0
|
144
|
gyorgy@0
|
145 def write_fsfile(self,fsFile,filetype = None):
|
gyorgy@0
|
146 '''Write file to session given as CGI field-storage object.'''
|
gyorgy@0
|
147 #fsFile is a cgi.FieldStorage object : cherrypy._cpcgifs.FieldStorage()
|
gyorgy@0
|
148 if hasattr (fsFile,'filename') and hasattr(fsFile,'file'):
|
gyorgy@0
|
149 return self._write_file(fsFile.file,fsFile.filename,filetype)
|
gyorgy@0
|
150
|
gyorgy@0
|
151 def write_fsfile_as(self,fsFile,filename,filetype = None):
|
gyorgy@0
|
152 '''Write file to session given as CGI field-storage object.'''
|
gyorgy@0
|
153 #fsFile is a cgi.FieldStorage object : cherrypy._cpcgifs.FieldStorage()
|
gyorgy@0
|
154 if hasattr (fsFile,'filename') and hasattr(fsFile,'file'):
|
gyorgy@0
|
155 return self._write_file(fsFile.file,filename,filetype)
|
gyorgy@0
|
156
|
gyorgy@0
|
157 def _write_file(self,file,filename,filetype):
|
gyorgy@0
|
158 '''Write file to session given as python file object.'''
|
gyorgy@0
|
159 filename = convertFilename(filename)
|
gyorgy@0
|
160 sessionPath = self.get_session_path()
|
gyorgy@0
|
161 filePath = os.path.join(sessionPath,filename)
|
gyorgy@0
|
162 # print 'writing file to: ',filePath
|
gyorgy@0
|
163
|
gyorgy@0
|
164 file.seek(0)
|
gyorgy@0
|
165 newFile = open(filePath,'wb')
|
gyorgy@0
|
166 try :
|
gyorgy@0
|
167 shutil.copyfileobj(file,newFile)
|
gyorgy@0
|
168 except :
|
gyorgy@0
|
169 try :
|
gyorgy@0
|
170 while True:
|
gyorgy@0
|
171 data = file.read(8192)
|
gyorgy@0
|
172 if not data: break
|
gyorgy@0
|
173 newFile.write(data)
|
gyorgy@0
|
174 except:
|
gyorgy@0
|
175 # newFile.close()
|
gyorgy@0
|
176 cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession: Could not write file to session: %i " %self.id)
|
gyorgy@0
|
177 raise IOError("Cherrypy::DirectorySession: Could not write file to session %r " %self.id)
|
gyorgy@0
|
178 finally:
|
gyorgy@0
|
179 newFile.close()
|
gyorgy@0
|
180
|
gyorgy@0
|
181 if not os.path.exists(filePath) \
|
gyorgy@0
|
182 or not os.stat(filePath).st_size > 0 :
|
gyorgy@0
|
183 cherrypy.log("Session x.xx: mxxx. Cherrypy::DirectorySession:_write_file: Warning: Failed to verify file at: %s " %filePath)
|
gyorgy@0
|
184 return False
|
gyorgy@0
|
185 elif not filetype :
|
gyorgy@0
|
186 return True
|
gyorgy@0
|
187 elif self.has_key(filetype) :
|
gyorgy@0
|
188 ol = self.get(filetype,None)
|
gyorgy@0
|
189 ol.append(os.path.basename(filePath))
|
gyorgy@0
|
190 self[filetype] = list(ol)
|
gyorgy@0
|
191 else :
|
gyorgy@0
|
192 self[filetype]= list([os.path.basename(filePath)])
|
gyorgy@0
|
193 self.save()
|
gyorgy@0
|
194 return True
|
gyorgy@0
|
195
|
gyorgy@0
|
196 def getFiles(self,filelist):
|
gyorgy@0
|
197 pass
|
gyorgy@0
|
198
|
gyorgy@0
|
199 def clean_up(self):
|
gyorgy@0
|
200 """Clean up expired sessions."""
|
gyorgy@0
|
201 now = datetime.datetime.now()
|
gyorgy@0
|
202 log_msg = "Session x.xx: mxxx. Cherrypy::DirectorySession: Cleaning up session data at: %s Length of cache = %i " %(str(now), len(self.cache))
|
gyorgy@0
|
203 print '\nCeaning up session data at %s: \n===========================' % now
|
gyorgy@0
|
204 print 'Length of cache = ', len(self.cache)
|
gyorgy@0
|
205 print 'cache members: ', self.cache.keys()
|
gyorgy@0
|
206 cherrypy.log(log_msg)
|
gyorgy@0
|
207
|
gyorgy@0
|
208 #clean up temp if server not in use
|
gyorgy@0
|
209 temp_path='/Users/Shared/george/sawa/sonic-annotator-webapp/static/temp/'
|
gyorgy@0
|
210 if len(self.cache.keys()) == 0 and os.path.exists(temp_path):
|
gyorgy@0
|
211 try :
|
gyorgy@0
|
212 for fname in os.listdir(temp_path):
|
gyorgy@0
|
213 os.remove(os.path.join(temp_path,fname))
|
gyorgy@0
|
214 print "Note: Cleaned temp directory."
|
gyorgy@0
|
215 except:
|
gyorgy@0
|
216 print "Warning: could not clear temp directory."
|
gyorgy@0
|
217 pass
|
gyorgy@0
|
218
|
gyorgy@0
|
219 for id, (data, expiration_time) in self.cache.items():
|
gyorgy@0
|
220 print 'Id: ', id
|
gyorgy@0
|
221 print 'expiration time: ', expiration_time
|
gyorgy@0
|
222 print 'Length of data: ', len(data)
|
gyorgy@0
|
223 print 'data members: ', data.keys()
|
gyorgy@0
|
224 print 'SESSIONS_PATH: ', self.SESSIONS_PATH
|
gyorgy@0
|
225
|
gyorgy@0
|
226 if expiration_time < now:
|
gyorgy@0
|
227
|
gyorgy@0
|
228 path = os.path.join(self.SESSIONS_PATH,str(id))
|
gyorgy@0
|
229 if os.path.exists(path) :
|
gyorgy@0
|
230 for fname in os.listdir(path):
|
gyorgy@0
|
231 os.remove(os.path.join(path,fname))
|
gyorgy@0
|
232 # os.removedirs(path) -> removes sessions too
|
gyorgy@0
|
233 os.rmdir(path)
|
gyorgy@0
|
234 try:
|
gyorgy@0
|
235 del self.cache[id]
|
gyorgy@0
|
236 except KeyError:
|
gyorgy@0
|
237 pass
|
gyorgy@0
|
238 try:
|
gyorgy@0
|
239 del self.locks[id]
|
gyorgy@0
|
240 except KeyError:
|
gyorgy@0
|
241 pass
|
gyorgy@0
|
242
|
gyorgy@0
|
243 def _exists(self):
|
gyorgy@0
|
244 return self.id in self.cache
|
gyorgy@0
|
245
|
gyorgy@0
|
246 def _load(self):
|
gyorgy@0
|
247 return self.cache.get(self.id)
|
gyorgy@0
|
248
|
gyorgy@0
|
249 #this is always called on an on_end_request hook (by cherrypy.session.save)
|
gyorgy@0
|
250 def _save(self, expiration_time):
|
gyorgy@0
|
251 print 'Saving Session ', self.id
|
gyorgy@0
|
252 self.cache[self.id] = (self._data, expiration_time)
|
gyorgy@0
|
253
|
gyorgy@0
|
254 def _delete(self):
|
gyorgy@0
|
255 del self.cache[self.id]
|
gyorgy@0
|
256
|
gyorgy@0
|
257 def acquire_lock(self):
|
gyorgy@0
|
258 """Acquire an exclusive lock on the currently-loaded session data."""
|
gyorgy@0
|
259 self.locked = True
|
gyorgy@0
|
260 self.locks.setdefault(self.id, threading.RLock()).acquire()
|
gyorgy@0
|
261
|
gyorgy@0
|
262 def release_lock(self):
|
gyorgy@0
|
263 """Release the lock on the currently-loaded session data."""
|
gyorgy@0
|
264 self.locks[self.id].release()
|
gyorgy@0
|
265 self.locked = False
|
gyorgy@0
|
266
|
gyorgy@0
|
267 def __len__(self):
|
gyorgy@0
|
268 """Return the number of active sessions."""
|
gyorgy@0
|
269 return len(self.cache)
|
gyorgy@0
|
270
|
gyorgy@0
|
271 cherrypy.lib.sessions.DirectorySession = DirectorySession
|