001: /*
002: * Copyright (c) JForum Team
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms,
006: * with or without modification, are permitted provided
007: * that the following conditions are met:
008: *
009: * 1) Redistributions of source code must retain the above
010: * copyright notice, this list of conditions and the
011: * following disclaimer.
012: * 2) Redistributions in binary form must reproduce the
013: * above copyright notice, this list of conditions and
014: * the following disclaimer in the documentation and/or
015: * other materials provided with the distribution.
016: * 3) Neither the name of "Rafael Steil" nor
017: * the names of its contributors may be used to endorse
018: * or promote products derived from this software without
019: * specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
022: * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
023: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
024: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
025: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR
026: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
028: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
029: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES
030: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
031: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
032: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
033: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
034: * IN CONTRACT, STRICT LIABILITY, OR TORT
035: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
036: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
037: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
038: *
039: * This file creation date: 12/03/2004 - 18:47:26
040: * The JForum Project
041: * http://www.jforum.net
042: */
043: package net.jforum;
044:
045: import java.sql.Connection;
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051:
052: import net.jforum.cache.CacheEngine;
053: import net.jforum.cache.Cacheable;
054: import net.jforum.dao.DataAccessDriver;
055: import net.jforum.entities.UserSession;
056: import net.jforum.repository.SecurityRepository;
057: import net.jforum.util.preferences.ConfigKeys;
058: import net.jforum.util.preferences.SystemGlobals;
059:
060: import org.apache.log4j.Logger;
061:
062: /**
063: * @author Rafael Steil
064: * @version $Id: SessionFacade.java,v 1.40 2007/09/20 16:07:10 rafaelsteil Exp $
065: */
066: public class SessionFacade implements Cacheable {
067: private static final Logger logger = Logger
068: .getLogger(SessionFacade.class);
069:
070: private static final String FQN = "sessions";
071: private static final String FQN_LOGGED = FQN + "/logged";
072: private static final String FQN_COUNT = FQN + "/count";
073: private static final String FQN_USER_ID = FQN + "/userId";
074: private static final String ANONYMOUS_COUNT = "anonymousCount";
075: private static final String LOGGED_COUNT = "loggedCount";
076:
077: private static CacheEngine cache;
078:
079: /**
080: * @see net.jforum.cache.Cacheable#setCacheEngine(net.jforum.cache.CacheEngine)
081: */
082: public void setCacheEngine(CacheEngine engine) {
083: cache = engine;
084: }
085:
086: /**
087: * Add a new <code>UserSession</code> entry to the session.
088: * This method will make a call to <code>JForum.getRequest.getSession().getId()</code>
089: * to retrieve the session's id
090: *
091: * @param us The user session objetc to add
092: * @see #add(UserSession, String)
093: */
094: public static void add(UserSession us) {
095: add(us, JForumExecutionContext.getRequest().getSessionContext()
096: .getId());
097: }
098:
099: /**
100: * Registers a new {@link UserSession}.
101: * <p>
102: * If a call to {@link UserSession#getUserId()} return a value different
103: * of <code>SystemGlobals.getIntValue(ConfigKeys.ANONYMOUS_USER_ID)</code>, then
104: * the user will be registered as "logged". Otherwise it will enter as anonymous.
105: * </p>
106: *
107: * <p>
108: * Please note that, in order to keep the number of guest and logged users correct,
109: * it's caller's responsability to {@link #remove(String)} the record before adding it
110: * again if the current session is currently represented as "guest".
111: * </p>
112: *
113: * @param us the UserSession to add
114: * @param sessionId the user's session id
115: */
116: public static void add(UserSession us, String sessionId) {
117: if (us.getSessionId() == null || us.getSessionId().equals("")) {
118: us.setSessionId(sessionId);
119: }
120:
121: synchronized (FQN) {
122: cache.add(FQN, us.getSessionId(), us);
123:
124: if (!JForumExecutionContext.getForumContext().isBot()) {
125: if (us.getUserId() != SystemGlobals
126: .getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) {
127: changeUserCount(LOGGED_COUNT, true);
128: cache.add(FQN_LOGGED, us.getSessionId(), us);
129: cache.add(FQN_USER_ID, Integer.toString(us
130: .getUserId()), us.getSessionId());
131: } else {
132: // TODO: check the anonymous IP constraint
133: changeUserCount(ANONYMOUS_COUNT, true);
134: }
135: }
136: }
137: }
138:
139: private static void changeUserCount(String cacheEntryName,
140: boolean increment) {
141: Integer count = (Integer) cache.get(FQN_COUNT, cacheEntryName);
142:
143: if (count == null) {
144: count = new Integer(0);
145: }
146:
147: if (increment) {
148: count = new Integer(count.intValue() + 1);
149: } else if (count.intValue() > 0) {
150: count = new Integer(count.intValue() - 1);
151: }
152:
153: cache.add(FQN_COUNT, cacheEntryName, count);
154: }
155:
156: /**
157: * Add a new entry to the user's session
158: *
159: * @param name The attribute name
160: * @param value The attribute value
161: */
162: public static void setAttribute(String name, Object value) {
163: JForumExecutionContext.getRequest().getSessionContext()
164: .setAttribute(name, value);
165: }
166:
167: /**
168: * Removes an attribute from the session
169: *
170: * @param name The key associated to the the attribute to remove
171: */
172: public static void removeAttribute(String name) {
173: JForumExecutionContext.getRequest().getSessionContext()
174: .removeAttribute(name);
175: }
176:
177: /**
178: * Gets an attribute value given its name
179: *
180: * @param name The attribute name to retrieve the value
181: * @return The value as an Object, or null if no entry was found
182: */
183: public static Object getAttribute(String name) {
184: return JForumExecutionContext.getRequest().getSessionContext()
185: .getAttribute(name);
186: }
187:
188: /**
189: * Remove an entry fro the session map
190: *
191: * @param sessionId The session id to remove
192: */
193: public static void remove(String sessionId) {
194: if (cache == null) {
195: logger.warn("Got a null cache instance. #" + sessionId);
196: return;
197: }
198:
199: logger.debug("Removing session " + sessionId);
200:
201: synchronized (FQN) {
202: UserSession us = getUserSession(sessionId);
203:
204: if (us != null) {
205: cache.remove(FQN_LOGGED, sessionId);
206: cache.remove(FQN_USER_ID, Integer.toString(us
207: .getUserId()));
208:
209: if (us.getUserId() != SystemGlobals
210: .getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) {
211: changeUserCount(LOGGED_COUNT, false);
212: } else {
213: changeUserCount(ANONYMOUS_COUNT, false);
214: }
215: }
216:
217: cache.remove(FQN, sessionId);
218: }
219: }
220:
221: /**
222: * Get all registered sessions
223: *
224: * @return <code>ArrayList</code> with the sessions. Each entry
225: * is an <code>UserSession</code> object.
226: */
227: public static List getAllSessions() {
228: synchronized (FQN) {
229: return new ArrayList(cache.getValues(FQN));
230: }
231: }
232:
233: /**
234: * Gets the {@link UserSession} instance of all logged users
235: * @return A list with the user sessions
236: */
237: public static List getLoggedSessions() {
238: synchronized (FQN) {
239: return new ArrayList(cache.getValues(FQN_LOGGED));
240: }
241: }
242:
243: /**
244: * Get the number of logged users
245: * @return the number of logged users
246: */
247: public static int registeredSize() {
248: Integer count = (Integer) cache.get(FQN_COUNT, LOGGED_COUNT);
249:
250: return (count == null ? 0 : count.intValue());
251: }
252:
253: /**
254: * Get the number of anonymous users
255: * @return the nuber of anonymous users
256: */
257: public static int anonymousSize() {
258: Integer count = (Integer) cache.get(FQN_COUNT, ANONYMOUS_COUNT);
259:
260: return (count == null ? 0 : count.intValue());
261: }
262:
263: public static void clear() {
264: synchronized (FQN) {
265: cache.add(FQN, new HashMap());
266: cache.add(FQN_COUNT, LOGGED_COUNT, new Integer(0));
267: cache.add(FQN_COUNT, ANONYMOUS_COUNT, new Integer(0));
268: cache.remove(FQN_LOGGED);
269: cache.remove(FQN_USER_ID);
270: }
271: }
272:
273: /**
274: * Gets the user's <code>UserSession</code> object
275: *
276: * @return The <code>UserSession</code> associated to the user's session
277: */
278: public static UserSession getUserSession() {
279: return getUserSession(JForumExecutionContext.getRequest()
280: .getSessionContext().getId());
281: }
282:
283: /**
284: * Gets an {@link UserSession} by the session id.
285: *
286: * @param sessionId the session's id
287: * @return an <b>immutable</b> UserSession, or <code>null</code> if no entry found
288: */
289: public static UserSession getUserSession(String sessionId) {
290: if (cache != null) {
291: UserSession us = (UserSession) cache.get(FQN, sessionId);
292: return (us != null ? us : null);
293: }
294:
295: logger
296: .warn("Got a null cache in getUserSession. #"
297: + sessionId);
298: return null;
299: }
300:
301: /**
302: * Gets the number of session elements.
303: *
304: * @return The number of session elements currently online (without bots)
305: */
306: public static int size() {
307: return (anonymousSize() + registeredSize());
308: }
309:
310: /**
311: * Verify if the user in already loaded
312: *
313: * @param username The username to check
314: * @return The session id if the user is already registered into the session,
315: * or <code>null</code> if it is not.
316: */
317: public static String isUserInSession(String username) {
318: int aid = SystemGlobals
319: .getIntValue(ConfigKeys.ANONYMOUS_USER_ID);
320:
321: synchronized (FQN) {
322: for (Iterator iter = cache.getValues(FQN).iterator(); iter
323: .hasNext();) {
324: UserSession us = (UserSession) iter.next();
325: String this Username = us.getUsername();
326:
327: if (this Username == null) {
328: continue;
329: }
330:
331: if (us.getUserId() != aid
332: && this Username.equals(username)) {
333: return us.getSessionId();
334: }
335: }
336: }
337:
338: return null;
339: }
340:
341: /**
342: * Verify if there is an user in the session with the
343: * user id passed as parameter.
344: *
345: * @param userId The user id to check for existance in the session
346: * @return The session id if the user is already registered into the session,
347: * or <code>null</code> if it is not.
348: */
349: public static String isUserInSession(int userId) {
350: return (String) cache
351: .get(FQN_USER_ID, Integer.toString(userId));
352: }
353:
354: /**
355: * Verify is the user is logged in.
356: *
357: * @return <code>true</code> if the user is logged, or <code>false</code> if is
358: * an anonymous user.
359: */
360: public static boolean isLogged() {
361: return "1"
362: .equals(SessionFacade.getAttribute(ConfigKeys.LOGGED));
363: }
364:
365: /**
366: * Marks the current user session as "logged" in
367: */
368: public static void makeLogged() {
369: SessionFacade.setAttribute(ConfigKeys.LOGGED, "1");
370: }
371:
372: /**
373: * Marks the current user session as "logged" out
374: *
375: */
376: public static void makeUnlogged() {
377: SessionFacade.removeAttribute(ConfigKeys.LOGGED);
378: }
379:
380: /**
381: * Returns a map containing information about read time of a set of topics.
382: * @return a map where the key is the topicId represented as an Integer, and the
383: * value is a Long representing the read time of such topic.
384: */
385: public static Map getTopicsReadTime() {
386: Map tracking = (Map) getAttribute(ConfigKeys.TOPICS_READ_TIME);
387:
388: if (tracking == null) {
389: tracking = new HashMap();
390: setAttribute(ConfigKeys.TOPICS_READ_TIME, tracking);
391: }
392:
393: return tracking;
394: }
395:
396: /**
397: * Returns a map with "all topics read" flags for some forum
398: * @return a map where the key is the forum id represented as an Integer,
399: * and the value is a Long representing the read time to be used in the verifications.
400: */
401: public static Map getTopicsReadTimeByForum() {
402: return (Map) getAttribute(ConfigKeys.TOPICS_READ_TIME_BY_FORUM);
403: }
404:
405: /**
406: * Persists user session information.
407: * This method will get a <code>Connection</code> making a call to
408: * <code>DBConnection.getImplementation().getConnection()</code>, and
409: * then releasing the connection after the method is processed.
410: *
411: * @param sessionId The session which we're going to persist information
412: * @see #storeSessionData(String, Connection)
413: */
414: public static void storeSessionData(String sessionId) {
415: Connection conn = null;
416: try {
417: conn = DBConnection.getImplementation().getConnection();
418: SessionFacade.storeSessionData(sessionId, conn);
419: } finally {
420: if (conn != null) {
421: try {
422: DBConnection.getImplementation().releaseConnection(
423: conn);
424: } catch (Exception e) {
425: logger.warn("Error while releasing a connection: "
426: + e);
427: }
428: }
429: }
430: }
431:
432: /**
433: * Persists user session information.
434: *
435: * @param sessionId The session which we're going to persist
436: * @param conn A <code>Connection</code> to be used to connect to
437: * the database.
438: * @see #storeSessionData(String)
439: */
440: public static void storeSessionData(String sessionId,
441: Connection conn) {
442: UserSession us = SessionFacade.getUserSession(sessionId);
443: if (us != null) {
444: try {
445: if (us.getUserId() != SystemGlobals
446: .getIntValue(ConfigKeys.ANONYMOUS_USER_ID)) {
447: DataAccessDriver.getInstance().newUserSessionDAO()
448: .update(us, conn);
449: }
450:
451: SecurityRepository.remove(us.getUserId());
452: } catch (Exception e) {
453: logger.warn("Error storing user session data: " + e, e);
454: }
455: }
456: }
457: }
|