import java.util.*;
import java.security.SecureRandom;
-public class Session implements java.io.Serializable {
- private static final Map<String, Session> sessions = new HashMap<String, Session>();
+public abstract class Session implements java.io.Serializable {
+ public static final ContextParam<Storage> store = new ContextParam<Storage>(new MemoryStorage());
private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>();
- private static final SecureRandom prng;
- private static long lastclean = 0;
- private final String id;
private final Map<Object, Object> props = new IdentityHashMap<Object, Object>();
- private long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
+ public long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
private Collection<Listener> ll = new HashSet<Listener>();
- static {
- try {
- prng = SecureRandom.getInstance("SHA1PRNG");
- } catch(java.security.NoSuchAlgorithmException e) {
- throw(new Error(e));
- }
- }
-
public static interface Listener {
public void destroy(Session sess);
}
- private Session(String id) {
- this.id = id;
- }
-
- public synchronized void listen(Listener l) {
- ll.add(l);
+ public static interface Storage {
+ public Session get(Request req);
}
- public synchronized Object get(Object key, Object def) {
- if(props.containsKey(key))
- return(props.get(key));
- else
- return(def);
- }
-
- public synchronized Object put(Object key, Object val) {
- return(props.put(key, val));
- }
-
- public void destroy() {
- synchronized(Session.class) {
- sessions.remove(id);
+ public static abstract class BaseStorage implements Storage {
+ private static final SecureRandom prng;
+
+ static {
+ try {
+ prng = SecureRandom.getInstance("SHA1PRNG");
+ } catch(java.security.NoSuchAlgorithmException e) {
+ throw(new Error(e));
+ }
+ }
+
+ public Session get(Request req) {
+ MultiMap<String, Cookie> cookies = Cookie.get(req);
+ Cookie sc = cookies.get("jsvc-session");
+
+ Session sess = null;
+ if(sc != null)
+ sess = get(sc.value);
+ if(sess == null) {
+ sess = create(req);
+ sc = new Cookie("jsvc-session", sess.id());
+ sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
+ sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
+ String pd = req.ctx().sysconfig("jsvc.session.domain", null);
+ if(pd != null)
+ sc.domain = pd;
+ sc.addto(req);
+ }
+ return(sess);
+ }
+
+ protected abstract Session get(String id);
+ protected abstract Session create(Request req);
+
+ public static String newid() {
+ byte[] rawid = new byte[16];
+ prng.nextBytes(rawid);
+ StringBuilder buf = new StringBuilder();
+ for(byte b : rawid) {
+ buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
+ buf.append(Misc.int2hex(b & 0x0f, false));
+ }
+ return(buf.toString());
}
- expire();
- }
-
- private synchronized void expire() {
- for(Listener l : ll)
- l.destroy(this);
}
- public synchronized static int num() {
- return(sessions.size());
- }
+ public static class MemoryStorage extends BaseStorage {
+ private final Map<Long, MemorySession> sessions = new HashMap<Long, MemorySession>();
+ private static long lastclean = 0;
+
+ private class MemorySession extends Session {
+ private final long id = BaseStorage.prng.nextLong();
+
+ private MemorySession(Request req) {
+ super(req);
+ }
+
+ public void destroy() {
+ synchronized(sessions) {
+ sessions.remove(id);
+ }
+ super.destroy();
+ }
+
+ private void expire() {
+ super.destroy();
+ }
+
+ public String id() {
+ return(Long.toString(id));
+ }
+ }
+
+ public int num() {
+ synchronized(sessions) {
+ return(sessions.size());
+ }
+ }
+
+ public Session get(String id) {
+ long idl;
+ try {
+ idl = Long.parseLong(id);
+ } catch(NumberFormatException e) {
+ return(null);
+ }
+ synchronized(sessions) {
+ return(sessions.get(idl));
+ }
+ }
+
+ public synchronized Session create(Request req) {
+ MemorySession sess = new MemorySession(req);
+ synchronized(sessions) {
+ sessions.put(sess.id, sess);
+ }
+ return(sess);
+ }
- private static String newid() {
- byte[] rawid = new byte[16];
- prng.nextBytes(rawid);
- StringBuilder buf = new StringBuilder();
- for(byte b : rawid) {
- buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
- buf.append(Misc.int2hex(b & 0x0f, false));
+ private void clean() {
+ long now = System.currentTimeMillis();
+ synchronized(sessions) {
+ for(Iterator<MemorySession> i = sessions.values().iterator(); i.hasNext();) {
+ MemorySession sess = i.next();
+ if(now > sess.atime + sess.etime) {
+ i.remove();
+ sess.expire();
+ }
+ }
+ }
+ }
+
+ public Session get(Request req) {
+ long now = System.currentTimeMillis();
+ if(now - lastclean > 3600 * 1000) {
+ clean();
+ lastclean = now;
+ }
+
+ return(super.get(req));
}
- return(buf.toString());
}
-
- private static Session create(Request req, String id) {
- Session sess = new Session(id);
- long etime = 0;
+
+ protected Session(Request req) {
int ct;
ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0"));
if(ct > 0)
- sess.etime = ct;
+ etime = ct;
ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0"));
if(ct > 0)
- sess.etime = ct;
- return(sess);
+ etime = ct;
}
- private synchronized static void clean() {
- long now = System.currentTimeMillis();
- for(Iterator<Session> i = sessions.values().iterator(); i.hasNext();) {
- Session sess = i.next();
- if(now > sess.atime + sess.etime) {
- i.remove();
- sess.expire();
- }
+ public abstract String id();
+
+ public void listen(Listener l) {
+ synchronized(ll) {
+ ll.add(l);
}
}
-
- public synchronized static Session get(Request req) {
- long now = System.currentTimeMillis();
- if(now - lastclean > 3600 * 1000) {
- clean();
- lastclean = now;
+
+ public Object get(Object key, Object def) {
+ synchronized(props) {
+ if(props.containsKey(key))
+ return(props.get(key));
+ else
+ return(def);
}
-
- Session sess = cache.get(req);
- if(sess != null) {
- sess.atime = System.currentTimeMillis();
- return(sess);
+ }
+
+ public synchronized Object put(Object key, Object val) {
+ return(props.put(key, val));
+ }
+
+ public void destroy() {
+ synchronized(ll) {
+ for(Listener l : ll)
+ l.destroy(this);
}
-
- MultiMap<String, Cookie> cookies = Cookie.get(req);
- Cookie sc = cookies.get("jsvc-session");
-
- if(sc != null)
- sess = sessions.get(sc.value);
- if(sess == null) {
- sess = create(req, newid());
- sessions.put(sess.id, sess);
- sc = new Cookie("jsvc-session", sess.id);
- sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
- sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
- String pd = req.ctx().sysconfig("jsvc.session.domain", null);
- if(pd != null)
- sc.domain = pd;
- sc.addto(req);
+ }
+
+ public static Session get(Request req) {
+ Session sess;
+ synchronized(req) {
+ synchronized(cache) {
+ sess = cache.get(req);
+ }
+ if(sess == null) {
+ sess = store.get().get(req);
+ synchronized(cache) {
+ cache.put(req, sess);
+ }
+ }
+ sess.atime = System.currentTimeMillis();
}
-
- cache.put(req, sess);
- sess.atime = System.currentTimeMillis();
return(sess);
}