001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2006 JSPWiki Development Team
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.auth;
021:
022: import java.security.Principal;
023: import java.util.*;
024:
025: import javax.servlet.http.HttpSession;
026: import javax.servlet.http.HttpSessionEvent;
027: import javax.servlet.http.HttpSessionListener;
028:
029: import org.apache.log4j.Logger;
030:
031: import com.ecyrd.jspwiki.WikiEngine;
032: import com.ecyrd.jspwiki.WikiSession;
033: import com.ecyrd.jspwiki.event.WikiEventListener;
034: import com.ecyrd.jspwiki.event.WikiEventManager;
035: import com.ecyrd.jspwiki.event.WikiSecurityEvent;
036: import com.ecyrd.jspwiki.rpc.json.JSONRPCManager;
037:
038: /**
039: * <p>Manages WikiSession's for different WikiEngine's.</p>
040: * <p>The WikiSession's are stored both in the remote user
041: * HttpSession and in the SessionMonitor for the WikeEngine.
042: * This class must be configured as a session listener in the
043: * web.xml for the wiki web application.
044: * </p>
045: */
046: public class SessionMonitor implements HttpSessionListener {
047: private static Logger log = Logger.getLogger(SessionMonitor.class);
048:
049: /** Map with WikiEngines as keys, and SessionMonitors as values. */
050: private static Map c_monitors = new HashMap();
051:
052: /** Weak hashmap with HttpSessions as keys, and WikiSessions as values. */
053: private final Map m_sessions = new WeakHashMap();
054:
055: private WikiEngine m_engine;
056:
057: private final PrincipalComparator m_comparator = new PrincipalComparator();
058:
059: /**
060: * Returns the instance of the SessionMonitor for this wiki.
061: * Only one SessionMonitor exists per WikiEngine.
062: * @param engine the wiki engine
063: * @return the session monitor
064: */
065: public static final SessionMonitor getInstance(WikiEngine engine) {
066: if (engine == null) {
067: throw new IllegalArgumentException("Engine cannot be null.");
068: }
069: SessionMonitor monitor;
070:
071: synchronized (c_monitors) {
072: monitor = (SessionMonitor) c_monitors.get(engine);
073: if (monitor == null) {
074: monitor = new SessionMonitor(engine);
075:
076: c_monitors.put(engine, monitor);
077: }
078: }
079: return monitor;
080: }
081:
082: /**
083: * Construct the SessionListener
084: */
085: public SessionMonitor() {
086: }
087:
088: private SessionMonitor(WikiEngine engine) {
089: m_engine = engine;
090: }
091:
092: /**
093: * Just looks for a WikiSession; does not create a new one.
094: * This method may return <code>null</code>, <em>and
095: * callers should check for this value</em>.
096: *
097: * @param session the user's HTTP session
098: * @return the WikiSession, if found
099: */
100: private WikiSession findSession(HttpSession session) {
101: WikiSession wikiSession = null;
102: String sid = (session == null) ? "(null)" : session.getId();
103: WikiSession storedSession = (WikiSession) m_sessions.get(sid);
104:
105: // If the weak reference returns a wiki session, return it
106: if (storedSession != null) {
107: if (log.isDebugEnabled()) {
108: log.debug("Looking up WikiSession for session ID="
109: + sid + "... found it");
110: }
111: wikiSession = storedSession;
112: }
113:
114: return wikiSession;
115: }
116:
117: /**
118: * <p>Looks up the wiki session associated with a user's Http session
119: * and adds it to the session cache. This method will return the
120: * "guest session" as constructed by {@link WikiSession#guestSession(WikiEngine)}
121: * if the HttpSession is not currently associated with a WikiSession.
122: * This method is guaranteed to return a non-<code>null</code> WikiSession.</p>
123: * <p>Internally, the session is stored in a HashMap; keys are
124: * the HttpSession objects, while the values are
125: * {@link java.lang.ref.WeakReference}-wrapped WikiSessions.</p>
126: * @param session the HTTP session
127: * @return the wiki session
128: */
129: public final WikiSession find(HttpSession session) {
130: WikiSession wikiSession = findSession(session);
131: String sid = (session == null) ? "(null)" : session.getId();
132:
133: // Otherwise, create a new guest session and stash it.
134: if (wikiSession == null) {
135: if (log.isDebugEnabled()) {
136: log.debug("Looking up WikiSession for session ID="
137: + sid
138: + "... not found. Creating guestSession()");
139: }
140: wikiSession = WikiSession.guestSession(m_engine);
141: synchronized (m_sessions) {
142: m_sessions.put(sid, wikiSession);
143: }
144: }
145:
146: return wikiSession;
147: }
148:
149: /**
150: * Removes the wiki session associated with the user's HttpSession
151: * from the session cache.
152: * @param session the user's HTTP session
153: */
154: public final void remove(HttpSession session) {
155: if (session == null) {
156: throw new IllegalArgumentException(
157: "Session cannot be null.");
158: }
159: synchronized (m_sessions) {
160: m_sessions.remove(session.getId());
161: }
162: }
163:
164: /**
165: * Returns the current number of active wiki sessions.
166: * @return the number of sessions
167: */
168: public final int sessions() {
169: return userPrincipals().length;
170: }
171:
172: /**
173: * <p>Returns the current wiki users as a sorted array of
174: * Principal objects. The principals are those returned by
175: * each WikiSession's {@link WikiSession#getUserPrincipal()}'s
176: * method.</p>
177: * <p>To obtain the list of current WikiSessions, we iterate
178: * through our session Map and obtain the list of values,
179: * which are WikiSessions wrapped in {@link java.lang.ref.WeakReference}
180: * objects. Those <code>WeakReference</code>s whose <code>get()</code>
181: * method returns non-<code>null</code> values are valid
182: * sessions.</p>
183: * @return the array of user principals
184: */
185: public final Principal[] userPrincipals() {
186: Collection principals = new ArrayList();
187: for (Iterator it = m_sessions.values().iterator(); it.hasNext();) {
188: WikiSession session = (WikiSession) it.next();
189:
190: principals.add(session.getUserPrincipal());
191: }
192: Principal[] p = (Principal[]) principals
193: .toArray(new Principal[principals.size()]);
194: Arrays.sort(p, m_comparator);
195: return p;
196: }
197:
198: /**
199: * Registers a WikiEventListener with this instance.
200: * @param listener the event listener
201: * @since 2.4.75
202: */
203: public final synchronized void addWikiEventListener(
204: WikiEventListener listener) {
205: WikiEventManager.addWikiEventListener(this , listener);
206: }
207:
208: /**
209: * Un-registers a WikiEventListener with this instance.
210: * @param listener the event listener
211: * @since 2.4.75
212: */
213: public final synchronized void removeWikiEventListener(
214: WikiEventListener listener) {
215: WikiEventManager.removeWikiEventListener(this , listener);
216: }
217:
218: /**
219: * Fires a WikiSecurityEvent to all registered listeners.
220: * @param type the event type
221: * @param principal the user principal associated with this session
222: * @param session the wiki session
223: * @since 2.4.75
224: */
225: protected final void fireEvent(int type, Principal principal,
226: WikiSession session) {
227: if (WikiEventManager.isListening(this )) {
228: WikiEventManager.fireEvent(this , new WikiSecurityEvent(
229: this , type, principal, session));
230: }
231: }
232:
233: /**
234: * Fires when the web container creates a new HTTP session.
235: *
236: * @param se the HTTP session event
237: */
238: public void sessionCreated(HttpSessionEvent se) {
239: HttpSession session = se.getSession();
240:
241: JSONRPCManager.sessionCreated(session);
242: }
243:
244: /**
245: * Removes the user's WikiSession from the internal session cache when the web
246: * container destoys an HTTP session.
247: * @param se the HTTP session event
248: */
249: public void sessionDestroyed(HttpSessionEvent se) {
250: HttpSession session = se.getSession();
251: Iterator it = c_monitors.values().iterator();
252: while (it.hasNext()) {
253: SessionMonitor monitor = (SessionMonitor) it.next();
254:
255: WikiSession storedSession = monitor.findSession(session);
256:
257: monitor.remove(session);
258:
259: log.debug("Removed session " + session.getId() + ".");
260:
261: if (storedSession != null) {
262: fireEvent(WikiSecurityEvent.SESSION_EXPIRED,
263: storedSession.getLoginPrincipal(),
264: storedSession);
265: }
266: }
267: }
268: }
|