Add simple SqlSession implementation
[cascardo/ipsilon.git] / ipsilon / util / sessions.py
diff --git a/ipsilon/util/sessions.py b/ipsilon/util/sessions.py
new file mode 100755 (executable)
index 0000000..b724471
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+
+import base64
+from cherrypy.lib.sessions import Session
+from ipsilon.util.data import SqlStore, SqlQuery
+import threading
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+
+SESSION_COLUMNS = ['id', 'data', 'expiration_time']
+
+
+class SqlSession(Session):
+
+    dburi = None
+    _db = None
+    _proto = 2
+    locks = {}
+
+    @classmethod
+    def setup(cls, **kwargs):
+        """Initialization from cherrypy"""
+
+        for k, v in kwargs.items():
+            if k == 'storage_dburi':
+                cls.dburi = v
+
+        cls._db = SqlStore(cls.dburi)
+
+    def _exists(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        result = q.select({'id': self.id})
+        return True if result.fetchone() else False
+
+    def _load(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        result = q.select({'id': self.id})
+        r = result.fetchone()
+        if r:
+            data = str(base64.b64decode(r[1]))
+            return pickle.loads(data)
+
+    def _save(self, expiration_time):
+        q = None
+        try:
+            q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS, trans=True)
+            q.delete({'id': self.id})
+            data = pickle.dumps((self._data, expiration_time), self._proto)
+            q.insert((self.id, base64.b64encode(data), expiration_time))
+            q.commit()
+        except Exception:  # pylint: disable=broad-except
+            if q:
+                q.rollback()
+            raise
+
+    def _delete(self):
+        q = SqlQuery(self._db, 'sessions', SESSION_COLUMNS)
+        q.delete({'id': self.id})
+
+    # copy what RamSession does for now
+    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