//
// Copyright (c) 2010, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 19 Mar 08 Brian Frank Creation
//
using concurrent
using web
**
** Default memory based web session storage
**
internal const class MemWispSessionStore : Actor, WispSessionStore
{
new make(WispService service) : super(ActorPool { it.name = "WispServiceSessions" })
{
this.service = service
}
override const WispService service
override Void onStart() { sendLater(houseKeepingPeriod, Msg("houseKeeping")) }
override Void onStop() { pool.stop }
override Str:Obj? load(Str id) { send(Msg("load", id)).get(timeout) }
override Void save(Str id, Str:Obj? map) { send(Msg("save", id, map)) }
override Void delete(Str id) { send(Msg("delete", id)) }
override Obj? receive(Obj? msgObj)
{
try
{
msg := (Msg)msgObj
// init or lookup map of sessions
sessions := Actor.locals["wisp.sessions"] as Str:MemStoreSession
if (sessions == null) Actor.locals["wisp.sessions"] = sessions = Str:MemStoreSession[:]
// dispatch msg to handler method
switch(msg.cmd)
{
case "houseKeeping": return onHouseKeeping(sessions)
case "load": return onLoad(sessions, msg)
case "save": return onSave(sessions, msg)
case "delete": return onDelete(sessions, msg)
}
echo("Unhandled msg: $msg.cmd")
}
catch (Err e) e.trace
return null
}
private Obj? onHouseKeeping(Str:MemStoreSession sessions)
{
// clean-up old sessions after expiration period
now := Duration.nowTicks
expired := Str[,]
sessions.each |session|
{
if (now - session.lastAccess > expirationPeriod.ticks)
expired.add(session.id)
}
expired.each |id| { sessions.remove(id) }
sendLater(houseKeepingPeriod, Msg("houseKeeping"))
return null
}
private Map onLoad(Str:MemStoreSession sessions, Msg msg)
{
sessions[msg.id]?.map ?: emptyMap
}
private Obj? onSave(Str:MemStoreSession sessions, Msg msg)
{
session := sessions[msg.id]
if (session == null) sessions[msg.id] = session = MemStoreSession(msg.id)
session.map = msg.map
session.lastAccess = Duration.nowTicks
return null
}
private Obj? onDelete(Str:MemStoreSession sessions, Msg msg)
{
sessions.remove(msg.id)
return null
}
const Duration houseKeepingPeriod := 1min
const Duration expirationPeriod := 24hr
const Duration timeout := 15sec
const Str:Obj? emptyMap := [:]
}
internal const class Msg
{
new make(Str c, Str? i := null, Map? m := null) { cmd = c; id = i; map = m }
const Str cmd
const Str? id
const Map? map
}
internal class MemStoreSession
{
new make(Str id) { this.id = id }
const Str id
Map? map
Int lastAccess
}