hsp-0.2: HSP/Data/Session.hs
-----------------------------------------------------------------------------
-- |
-- Module : HSP.Data.Session
-- Copyright : (c) Niklas Broberg 2004,
-- License : BSD-style (see the file LICENSE.txt)
--
-- Maintainer : Niklas Broberg, d00nibro@dtek.chalmers.se
-- Stability : experimental
-- Portability : requires undecidable and overlapping instances
--
-- Defines the Session object available to HSP pages.
-----------------------------------------------------------------------------
module HSP.Data.Session (
Session -- ^ Abstract
-- * Functions used in HSP
, getVarValue -- ^ :: Session -> Key -> IO (Maybe Value)
, setVarValue -- ^ :: Session -> Key -> Value -> IO ()
, deleteVar -- ^ :: Session -> Key -> IO ()
, abandon -- ^ :: Session -> IO ()
, setExpires -- ^ :: Session -> CalendarTime -> IO ()
-- * Functions used by the RTS
, isSession -- ^ :: Session -> IO Bool
, getSessionId -- ^ :: Session -> IO (Maybe SessionId)
, getExpires -- ^ :: Session -> IO Expires
, initSession -- ^ :: [(Key, Value)] -> IO Session
, noSession -- ^ :: IO Session
, getNewVars -- ^ :: Session -> IO [(Key, (Value, Expires))]
, getUpdatedVars -- ^ :: Session -> IO [(Key, (Value, Expires))]
, getDeletedVars -- ^ :: Session -> IO [Key]
) where
import qualified Data.HashTable as HT
import Control.Concurrent.MVar
import System.Time
-------------------------------------
-- Help types
type Expires = Maybe CalendarTime
type Key = String
type Value = String
type SessionId = Int
neverExpire :: Expires
neverExpire = Nothing
expire :: CalendarTime -> Expires
expire = Just
data Status = New | Orig | Updated | Deleted
deriving (Eq)
updateStatus :: Status -> Status
updateStatus s = case s of
New -> New
_ -> Updated
----------------------------------------
-- The main datatypes
-- | The 'Session' datatype is basically a data repository.
-- To keep tracks of updates, we use an extra repository.
newtype Session = MkSession (MVar (Maybe SessionData))
data SessionData = MkSessionData {
sessionId :: MVar (Maybe SessionId),
expires :: MVar Expires,
dataRep :: HT.HashTable Key (Value,Expires,Status)
}
-- | Create a new 'Session' object with initial data.
initSession :: SessionId -> Expires -> [(Key, (Value, Expires))] -> IO Session
initSession sid exps initData = do
let dat = map (\(k,(v,e)) -> (k,(v,e,Orig))) initData
rep <- HT.fromList HT.hashString dat
exp <- newMVar exps
si <- newMVar $ Just sid
let sd = MkSessionData {
dataRep = rep,
expires = exp,
sessionId = si }
mvSess <- newMVar $ Just sd
return $ MkSession mvSess
noSession :: IO Session
noSession = do
mvs <- newMVar Nothing
return $ MkSession mvs
---------------------------------------
-- Operate on sessions
-- | Retrieve the value of a variable in the repository.
getVarValue :: Session -> Key -> IO (Maybe Value)
getVarValue (MkSession mvSess) k = do
msess <- takeMVar mvSess
res <- case msess of
Just sd -> do mv <- HT.lookup (dataRep sd) k
case mv of
Nothing -> return Nothing
Just (v,_,st) | st /= Deleted
-> return $ Just v
_ -> return $ Nothing
putMVar mvSess msess
return res
setVarValue :: Session -> Key -> Value -> IO ()
setVarValue (MkSession mvSess) k v = do
msess <- takeMVar mvSess
case msess of
Nothing -> do
dr <- HT.fromList HT.hashString [(k, (v, neverExpire, New))]
exp <- newMVar neverExpire
si <- newMVar Nothing
putMVar mvSess $ Just $ MkSessionData si exp dr
Just sd -> do
mv <- HT.lookup (dataRep sd) k
case mv of
Nothing -> HT.insert (dataRep sd) k (v, neverExpire, New)
Just (_,e,st) -> HT.insert (dataRep sd) k (v, e, updateStatus st)
putMVar mvSess msess
deleteVar :: Session -> Key -> IO ()
deleteVar (MkSession mvSess) k = do
msess <- takeMVar mvSess
case msess of
Nothing -> return ()
Just sd -> HT.delete (dataRep sd) k
putMVar mvSess msess
abandon :: Session -> IO ()
abandon (MkSession mvs) = modifyMVar_ mvs (\_ -> return Nothing)
setExpires :: Session -> CalendarTime -> IO ()
setExpires (MkSession mvs) ct = do
msess <- takeMVar mvs
case msess of
Nothing -> do
dr <- HT.new (==) HT.hashString
si <- newMVar Nothing
exp <- newMVar $ expire ct
putMVar mvs (Just $ MkSessionData si exp dr)
Just sd -> do
modifyMVar_ (expires sd) (\_ -> return $ expire ct)
putMVar mvs msess
-----------------------------------------
-- Used by HSPR
isSession :: Session -> IO Bool
isSession s = withSession s $ \ms ->
case ms of
Nothing -> return False
Just _ -> return True
getSessionId :: Session -> IO (Maybe SessionId)
getSessionId s = withSession s $ \ms ->
case ms of
Nothing -> return Nothing
Just s -> readMVar (sessionId s)
getExpires :: Session -> IO Expires
getExpires s = withSession s $ \ms ->
case ms of
Nothing -> return Nothing
Just s -> readMVar (expires s)
getVars :: Status -> Session -> IO [(Key, (Value, Expires))]
getVars status s = withSession s $ \msess ->
case msess of
Nothing -> return []
Just sd -> do
vals <- HT.toList (dataRep sd)
let newVals = filter (\(_,(_,_,st)) -> st == status) vals
return $ map (\(k,(v,e,_)) -> (k,(v,e))) newVals
getNewVars, getUpdatedVars :: Session -> IO [(Key, (Value, Expires))]
getNewVars = getVars New
getUpdatedVars = getVars Updated
getDeletedVars :: Session -> IO [Key]
getDeletedVars s = do
vals <- getVars Deleted s
return $ map fst vals
withSession :: Session -> (Maybe SessionData -> IO a) -> IO a
withSession (MkSession mvs) f = do
ms <- takeMVar mvs
r <- f ms
putMVar mvs ms
return r