| SessionManager.java |
1 // Copyright (c) 2000 Just Objects B.V. <just@justobjects.nl>
2 // Distributable under LGPL license. See terms of license at gnu.org.
3
4 package nl.justobjects.pushlet.core;
5
6 import nl.justobjects.pushlet.util.Log;
7 import nl.justobjects.pushlet.util.PushletException;
8 import nl.justobjects.pushlet.util.Rand;
9 import nl.justobjects.pushlet.util.Sys;
10
11import java.rmi.server.UID;
12import java.util.*;
13
14/**
15 * Manages lifecycle of Sessions.
16 *
17 * @author Just van den Broecke - Just Objects ©
18 * @version $Id: SessionManager.java,v 1.11 2007/11/23 14:33:07 justb Exp $
19 */
20public class SessionManager implements ConfigDefs {
21
22 /**
23 * Singleton pattern: single instance.
24 */
25 private static SessionManager instance;
26
27 static {
28 // Singleton + factory pattern: create single instance
29 // from configured class name
30 try {
31 instance = (SessionManager) Config.getClass(SESSION_MANAGER_CLASS, "nl.justobjects.pushlet.core.SessionManager").newInstance();
32 Log.info("SessionManager created className=" + instance.getClass());
33 } catch (Throwable t) {
34 Log.fatal("Cannot instantiate SessionManager from config", t);
35 }
36 }
37
38 /**
39 * Timer to schedule session leasing TimerTasks.
40 */
41 private Timer timer;
42 private final long TIMER_INTERVAL_MILLIS = 60000;
43
44 /**
45 * Map of active sessions, keyed by their id.
46 */
47 private Map sessions = Collections.synchronizedMap(new HashMap(13));
48
49 /**
50 * Shadow cache of active Sessions.
51 */
52 private Session[] sessionCache = new Session[0];
53
54 /**
55 * Flag indicating subscriptions have changed.
56 */
57 private volatile boolean sessionCacheDirty = false;
58
59 /**
60 * Singleton pattern: protected constructor needed for derived classes.
61 */
62 protected SessionManager() {
63 }
64
65 /**
66 * Create new Session (but add later).
67 */
68 public Session createSession(Event anEvent) throws PushletException {
69 // Trivial
70 return Session.create(createSessionId());
71 }
72
73 /**
74 * Create unique Session id.
75 */
76 public String createSessionId() {
77 // Use UUID if specified in config
78 if (Config.hasProperty(SESSION_ID_GENERATION) && Config.getProperty(SESSION_ID_GENERATION).equals(SESSION_ID_GENERATION_UUID)) {
79 return new UID().toString();
80 }
81
82 // Other cases use random name
83
84 // Create a unique session id
85 // In 99.9999 % of the cases this should be generated at once
86 String id = null;
87 while (true) {
88 id = Rand.randomName(Config.getIntProperty(SESSION_ID_SIZE));
89 if (!hasSession(id)) {
90 // Created unique session id
91 break;
92 }
93 }
94 return id;
95 }
96
97 /**
98 * Singleton pattern: get single instance.
99 */
00 public static SessionManager getInstance() {
01 return instance;
02 }
03
04 /**
05 * Get number of listening Sessions.
06 */
07 public Session getSession(String anId) {
08 return (Session) sessions.get(anId);
09 }
10
11 /**
12 * Get copy of listening Sessions.
13 */
14 public Session[] getSessions() {
15 return (Session[]) sessions.values().toArray(new Session[0]);
16 }
17
18 /**
19 * Get number of listening Sessions.
20 */
21 public int getSessionCount() {
22 return sessions.size();
23 }
24
25 /**
26 * Get status info.
27 */
28 public String getStatus() {
29 Session[] sessions = getSessions();
30 String statusInfo = "SessionMgr: " + sessions.length + " sessions \\n";
31 for (int i = 0; i < sessions.length; i++) {
32 statusInfo = statusInfo + sessions[i] + "\\n";
33 }
34 return statusInfo;
35 }
36
37 /**
38 * Is Session present?.
39 */
40 public boolean hasSession(String anId) {
41 return sessions.containsKey(anId);
42 }
43
44 /**
45 * Add session.
46 */
47 public void addSession(Session session) {
48 // log(session.getId() + " at " + session.getAddress() + " adding ");
49 sessions.put(session.getId(), session);
50 sessionCacheDirty = true;
51 info(session.getId() + " at " + session.getAddress() + " added ");
52 }
53
54 /**
55 * Register session for removal.
56 */
57 public Session removeSession(Session aSession) {
58 Session session = (Session) sessions.remove(aSession.getId());
59 if (session != null) {
60 sessionCacheDirty = true;
61 info(session.getId() + " at " + session.getAddress() + " removed ");
62 }
63 return session;
64 }
65
66 public Session[] getSnapshot() {
67 // If no session change return immediately.
68 if (!sessionCacheDirty) {
69 return sessionCache;
70 }
71
72 // Session cache is dirty: recreate
73 synchronized (sessionCache) {
74 // ASSERT: cache is dirty, need to update cache
75
76 // Session cache array may be reused: make all entries null.
77 for (int i = 0; i < sessionCache.length; i++) {
78 sessionCache[i] = null;
79 }
80
81 // Copy all sessions into cache
82 // toArray() expands cache size if required
83 sessionCache = (Session[]) sessions.values().toArray(sessionCache);
84
85 // Mark session cache actualized
86 sessionCacheDirty = false;
87
88 return sessionCache;
89 }
90 }
91
92
93 /**
94 * Util: stdout printing.
95 */
96 public void start() {
97 if (timer != null) {
98 stop();
99 }
00 timer = new Timer(false);
01 timer.schedule(new AgingTimerTask(), TIMER_INTERVAL_MILLIS, TIMER_INTERVAL_MILLIS);
02 info("started; interval=" + TIMER_INTERVAL_MILLIS + "ms");
03 }
04
05 /**
06 * Util: stdout printing.
07 */
08 public void stop() {
09 if (timer != null) {
10 timer.cancel();
11 timer = null;
12 }
13 sessions.clear();
14 sessionCache = new Session[0];
15 info("stopped");
16 }
17
18 /**
19 * Util: stdout printing.
20 */
21 private void info(String s) {
22 Log.info("SessionManager: " + new Date() + " " + s);
23 }
24
25 /**
26 * Util: stdout printing.
27 */
28 private void warn(String s) {
29 Log.warn("SessionManager: " + s);
30 }
31
32 /**
33 * Util: stdout printing.
34 */
35 private void debug(String s) {
36 Log.debug("SessionManager: " + s);
37 }
38
39 /**
40 * Manages session timeouts.
41 */
42 private class AgingTimerTask extends TimerTask {
43 private long lastRun = Sys.now();
44
45 public void run() {
46 long now = Sys.now();
47 long delta = now - lastRun;
48 lastRun = now;
49 // info("tick " + delta);
50
51 Session[] sessions = getSnapshot();
52 Session nextSession = null;
53 for (int i = 0; i < sessions.length; i++) {
54 nextSession = sessions[i];
55
56 // Null denotes end of cache array
57 if (nextSession == null) {
58 break;
59 }
60
61 try {
62 // Age the lease
63 nextSession.age(delta);
64
65 // Stop session if lease expired
66 if (nextSession.isExpired()) {
67 info("Session expired: " + nextSession);
68 nextSession.stop();
69 }
70 } catch (Throwable t) {
71 warn("Error in timer task : " + t);
72 }
73 }
74 }
75 }
76}
77
78/*
79 * $Log: SessionManager.java,v $
80 * Revision 1.11 2007/11/23 14:33:07 justb
81 * core classes now configurable through factory
82 *
83 * Revision 1.10 2007/11/10 14:47:45 justb
84 * make session key generation configurable (can use uuid)
85 *
86 * Revision 1.9 2007/11/10 14:17:18 justb
87 * minor cosmetic changes just commit now
88 *
89 * Revision 1.8 2007/07/02 08:12:16 justb
90 * redo to original version of session cache (with break, but nullify array first)
91 *
92 * Revision 1.7 2007/07/02 07:33:02 justb
93 * small fix in sessionmgr for holes in sessioncache array (continue i.s.o. break)
94 *
95 * Revision 1.6 2006/11/18 12:13:47 justb
96 * made SessionManager constructor protected to allow constructing derived classes
97 *
98 * Revision 1.5 2005/02/28 15:58:05 justb
99 * added SimpleListener example
00 *
01 * Revision 1.4 2005/02/28 12:45:59 justb
02 * introduced Command class
03 *
04 * Revision 1.3 2005/02/28 09:14:55 justb
05 * sessmgr/dispatcher factory/singleton support
06 *
07 * Revision 1.2 2005/02/25 15:13:01 justb
08 * session id generation more robust
09 *
10 * Revision 1.1 2005/02/21 16:59:09 justb
11 * SessionManager and session lease introduced
12 *
13
14 *
15 */
16| SessionManager.java |