From: Fredrik Tolf Date: Tue, 29 Mar 2011 06:21:30 +0000 (+0200) Subject: Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc X-Git-Url: http://dolda2000.com/gitweb/?p=jsvc.git;a=commitdiff_plain;h=HEAD;hp=f8ade4855828e4e9a57b6c9bee360d26dca6ea34 Merge branch 'master' of git.dolda2000.com:/srv/git/r/jsvc --- diff --git a/src/dolda/jsvc/ContextResponder.java b/src/dolda/jsvc/ContextResponder.java deleted file mode 100644 index 1ff7b44..0000000 --- a/src/dolda/jsvc/ContextResponder.java +++ /dev/null @@ -1,5 +0,0 @@ -package dolda.jsvc; - -public interface ContextResponder extends Responder { - public void destroy(); -} diff --git a/src/dolda/jsvc/Destroyable.java b/src/dolda/jsvc/Destroyable.java new file mode 100644 index 0000000..deb90eb --- /dev/null +++ b/src/dolda/jsvc/Destroyable.java @@ -0,0 +1,5 @@ +package dolda.jsvc; + +public interface Destroyable { + public void destroy(); +} diff --git a/src/dolda/jsvc/ThreadContext.java b/src/dolda/jsvc/ThreadContext.java index 8e544d3..6eb7d87 100644 --- a/src/dolda/jsvc/ThreadContext.java +++ b/src/dolda/jsvc/ThreadContext.java @@ -118,8 +118,8 @@ public class ThreadContext extends ThreadGroup { } public void shutdown() { - if(root instanceof ContextResponder) - ((ContextResponder)root).destroy(); + if(root instanceof Destroyable) + ((Destroyable)root).destroy(); try { long last = 0; while(true) { diff --git a/src/dolda/jsvc/util/PerSession.java b/src/dolda/jsvc/util/PerSession.java index 562e2c9..d4e4d73 100644 --- a/src/dolda/jsvc/util/PerSession.java +++ b/src/dolda/jsvc/util/PerSession.java @@ -93,14 +93,6 @@ public class PerSession implements Responder { if(resp == null) { resp = create(sess); sess.put(rcl, resp); - if(resp instanceof ContextResponder) { - final ContextResponder cr = (ContextResponder)resp; - sess.listen(new Session.Listener() { - public void destroy(Session sess) { - cr.destroy(); - } - }); - } } } resp.respond(req); diff --git a/src/dolda/jsvc/util/Session.java b/src/dolda/jsvc/util/Session.java index eb17fcf..7b87d12 100644 --- a/src/dolda/jsvc/util/Session.java +++ b/src/dolda/jsvc/util/Session.java @@ -4,41 +4,158 @@ import dolda.jsvc.*; import java.util.*; import java.security.SecureRandom; -public class Session implements java.io.Serializable { - private static final Map sessions = new HashMap(); +public abstract class Session implements java.io.Serializable { + public static final ContextParam store = new ContextParam(new MemoryStorage()); private static final Map cache = new WeakHashMap(); - private static final SecureRandom prng; - private static long lastclean = 0; - private final String id; private final Map props = new IdentityHashMap(); - private long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000; - private Collection ll = new HashSet(); + public long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000; - static { - try { - prng = SecureRandom.getInstance("SHA1PRNG"); - } catch(java.security.NoSuchAlgorithmException e) { - throw(new Error(e)); - } + public static interface Storage { + public Session get(Request req); } - public static interface Listener { - public void destroy(Session sess); + 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 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()); + } } - private Session(String id) { - this.id = id; + public static class MemoryStorage extends BaseStorage { + private final Map sessions = new HashMap(); + 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 void clean() { + long now = System.currentTimeMillis(); + synchronized(sessions) { + for(Iterator 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)); + } } - public synchronized void listen(Listener l) { - ll.add(l); + protected Session(Request req) { + int ct; + ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0")); + if(ct > 0) + etime = ct; + ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0")); + if(ct > 0) + etime = ct; } - public synchronized Object get(Object key, Object def) { - if(props.containsKey(key)) - return(props.get(key)); - else - return(def); + public abstract String id(); + + public Object get(Object key, Object def) { + synchronized(props) { + if(props.containsKey(key)) + return(props.get(key)); + else + return(def); + } } public synchronized Object put(Object key, Object val) { @@ -46,88 +163,28 @@ public class Session implements java.io.Serializable { } public void destroy() { - synchronized(Session.class) { - sessions.remove(id); + synchronized(props) { + for(Object val : props.values()) { + if(val instanceof Destroyable) + ((Destroyable)val).destroy(); + } } - expire(); } - private synchronized void expire() { - for(Listener l : ll) - l.destroy(this); - } - - public synchronized static int num() { - return(sessions.size()); - } - - 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)); - } - return(buf.toString()); - } - - private static Session create(Request req, String id) { - Session sess = new Session(id); - long etime = 0; - int ct; - ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0")); - if(ct > 0) - sess.etime = ct; - ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0")); - if(ct > 0) - sess.etime = ct; - return(sess); - } - - private synchronized static void clean() { - long now = System.currentTimeMillis(); - for(Iterator i = sessions.values().iterator(); i.hasNext();) { - Session sess = i.next(); - if(now > sess.atime + sess.etime) { - i.remove(); - sess.expire(); + 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); + } } - } - } - - public synchronized static Session get(Request req) { - long now = System.currentTimeMillis(); - if(now - lastclean > 3600 * 1000) { - clean(); - lastclean = now; - } - - Session sess = cache.get(req); - if(sess != null) { sess.atime = System.currentTimeMillis(); - return(sess); } - - MultiMap 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); - } - - cache.put(req, sess); - sess.atime = System.currentTimeMillis(); return(sess); }