1 package dolda.jsvc.util;
5 import java.security.SecureRandom;
7 public abstract class Session implements java.io.Serializable {
8 public static final ContextParam<Storage> store = new ContextParam<Storage>(new MemoryStorage());
9 private static final Map<Request, Session> cache = new WeakHashMap<Request, Session>();
10 private final Map<Object, Object> props = new IdentityHashMap<Object, Object>();
11 public long ctime = System.currentTimeMillis(), atime = ctime, etime = 86400 * 1000;
13 public static interface Storage {
14 public Session get(Request req);
17 public static abstract class BaseStorage implements Storage {
18 private static final SecureRandom prng;
22 prng = SecureRandom.getInstance("SHA1PRNG");
23 } catch(java.security.NoSuchAlgorithmException e) {
28 public Session get(Request req) {
29 MultiMap<String, Cookie> cookies = Cookie.get(req);
30 Cookie sc = cookies.get("jsvc-session");
37 sc = new Cookie("jsvc-session", sess.id());
38 sc.expires = new Date(System.currentTimeMillis() + (86400L * 365L * 1000L));
39 sc.path = req.ctx().sysconfig("jsvc.session.path", req.rooturl().getPath());
40 String pd = req.ctx().sysconfig("jsvc.session.domain", null);
48 protected abstract Session get(String id);
49 protected abstract Session create(Request req);
51 public static String newid() {
52 byte[] rawid = new byte[16];
53 prng.nextBytes(rawid);
54 StringBuilder buf = new StringBuilder();
56 buf.append(Misc.int2hex((b & 0xf0) >> 4, false));
57 buf.append(Misc.int2hex(b & 0x0f, false));
59 return(buf.toString());
63 public static class MemoryStorage extends BaseStorage {
64 private final Map<Long, MemorySession> sessions = new HashMap<Long, MemorySession>();
65 private static long lastclean = 0;
67 private class MemorySession extends Session {
68 private final long id = BaseStorage.prng.nextLong();
70 private MemorySession(Request req) {
74 public void destroy() {
75 synchronized(sessions) {
81 private void expire() {
86 return(Long.toString(id));
91 synchronized(sessions) {
92 return(sessions.size());
96 public Session get(String id) {
99 idl = Long.parseLong(id);
100 } catch(NumberFormatException e) {
103 synchronized(sessions) {
104 return(sessions.get(idl));
108 public synchronized Session create(Request req) {
109 MemorySession sess = new MemorySession(req);
110 synchronized(sessions) {
111 sessions.put(sess.id, sess);
116 private void clean() {
117 long now = System.currentTimeMillis();
118 synchronized(sessions) {
119 for(Iterator<MemorySession> i = sessions.values().iterator(); i.hasNext();) {
120 MemorySession sess = i.next();
121 if(now > sess.atime + sess.etime) {
129 public Session get(Request req) {
130 long now = System.currentTimeMillis();
131 if(now - lastclean > 3600 * 1000) {
136 return(super.get(req));
140 protected Session(Request req) {
142 ct = Integer.parseInt(req.ctx().libconfig("jsvc.session.expire", "0"));
145 ct = Integer.parseInt(req.ctx().sysconfig("jsvc.session.expire", "0"));
150 public abstract String id();
152 public Object get(Object key, Object def) {
153 synchronized(props) {
154 if(props.containsKey(key))
155 return(props.get(key));
161 public synchronized Object put(Object key, Object val) {
162 return(props.put(key, val));
165 public void destroy() {
166 synchronized(props) {
167 for(Object val : props.values()) {
168 if(val instanceof Destroyable)
169 ((Destroyable)val).destroy();
174 public static Session get(Request req) {
177 synchronized(cache) {
178 sess = cache.get(req);
181 sess = store.get().get(req);
182 synchronized(cache) {
183 cache.put(req, sess);
186 sess.atime = System.currentTimeMillis();
191 public static Session get() {
192 return(get(RequestThread.request()));