001: /*
002: * JOSSO: Java Open Single Sign-On
003: *
004: * Copyright 2004-2008, Atricore, Inc.
005: *
006: * This is free software; you can redistribute it and/or modify it
007: * under the terms of the GNU Lesser General Public License as
008: * published by the Free Software Foundation; either version 2.1 of
009: * the License, or (at your option) any later version.
010: *
011: * This software 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 GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this software; if not, write to the Free
018: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
020: */
021:
022: package org.josso.gateway.session.service.store.db;
023:
024: import java.sql.Connection;
025: import java.sql.PreparedStatement;
026: import java.sql.ResultSet;
027: import java.sql.SQLException;
028: import java.sql.Statement;
029: import java.util.ArrayList;
030: import java.util.Date;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.josso.gateway.session.exceptions.SSOSessionException;
035: import org.josso.gateway.session.service.BaseSession;
036: import org.josso.gateway.session.service.MutableBaseSession;
037: import org.josso.gateway.session.service.store.SessionStore;
038: import org.josso.gateway.session.service.store.AbstractSessionStore;
039:
040: /**
041: * An abstraction of a SessionStore backed by a database.
042: *
043: * <p>
044: * Additional component properties include:
045: * <ul>
046: * <li>sizeQuery = The SQL Query used to add a new session to the store.</li>
047: * <li>keysQuery = The SQL Query used to retrieve all session ids. The first column for each row in the result set must be the session id.</li>
048: * <li>loadAllQuery = The SQL Query used to load all sessions from the store.</li>
049: * <li>loadQuery = The SQL Query used to load one session from the store based on its id.</li>
050: * <li>loadByUserNameQuery = The SQL Query used to load all sessions associated to a given user.</li>
051: * <li>loadByLastAccesstimeQuery = The SQL Query used to load all sessions last accessed before the given date.</li>
052: * <li>loadByValidQuery = The SQL Query used to load all sessions whose valid property is equals to the gvien argument.</li>
053: * <li>deleteDml = The SQL Query used to remove a session from the store.</li>
054: * <li>deletAllDml = The SQL Query used to remove ALL sessions from the store.</li>
055: * <li>insertDml = The SQL Query used to add a new session to the store.</li>
056: * </ul>
057: * </p>
058: * <p>
059: * The columns in the result set for all load methods must be in the following order :
060: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
061: * </p>
062: * <p>
063: * lastAccessTime and creationTime are treated as a longs, not dates.
064: * </p>
065: *
066: * @author Jeff Gutierrez (code@gutierrez.ph) ca
067: *
068: */
069: public abstract class DbSessionStore extends AbstractSessionStore {
070: private static final Log __log = LogFactory
071: .getLog(DbSessionStore.class);
072:
073: private String _sizeQuery = null;
074: private String _keysQuery = null;
075: private String _loadAllQuery = null;
076: private String _loadQuery = null;
077: private String _loadByUserNameQuery = null;
078: private String _loadByLastAccessTimeQuery = null;
079: private String _loadByValidQuery = null;
080:
081: private String _deleteDml = null;
082: private String _deleteAllDml = null;
083: private String _insertDml = null;
084:
085: // -------------------------------------
086: // DbSessinStore-specific
087: // -------------------------------------
088:
089: /**
090: * Implementation classes implement this method.
091: *
092: * @return A database connection.
093: */
094: protected abstract Connection getConnection() throws SQLException,
095: SSOSessionException;
096:
097: /**
098: * Close the given db connection.
099: *
100: * @param dbConnection
101: */
102: protected void close(Connection dbConnection)
103: throws SSOSessionException {
104: try {
105: if (dbConnection != null && !dbConnection.isClosed()) {
106: dbConnection.close();
107: }
108: } catch (SQLException se) {
109: if (__log.isDebugEnabled())
110: __log.debug("Error while clossing connection");
111:
112: throw new SSOSessionException(
113: "Error while clossing connection\n"
114: + se.getMessage());
115: } catch (Exception e) {
116: if (__log.isDebugEnabled())
117: __log.debug("Error while clossing connection");
118:
119: throw new SSOSessionException(
120: "Error while clossing connection\n"
121: + e.getMessage());
122: }
123:
124: }
125:
126: // -------------------------------------------------
127: // Configuration properties
128: // -------------------------------------------------
129:
130: /**
131: * The SQL Query used to add a new session to the store.
132: *
133: * @param query
134: */
135: public void setInsertDml(String query) {
136: _insertDml = query;
137: }
138:
139: public String getInsertDml() {
140: return _insertDml;
141: }
142:
143: /**
144: * The SQL Query used to remove ALL sessions from the store.
145: *
146: */
147: public void setDeleteAllDml(String query) {
148: _deleteAllDml = query;
149: }
150:
151: public String getDeleteAllDml() {
152: return _deleteAllDml;
153: }
154:
155: /**
156: * The SQL Query used to remove a session from the store.
157: *
158: */
159: public void setDeleteDml(String query) {
160: _deleteDml = query;
161: }
162:
163: public String getDeleteDml() {
164: return _deleteDml;
165: }
166:
167: /**
168: * The SQL Query used to load all sessions last accessed before the given date.
169: *
170: * The columns in the result set must be in the following order :
171: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
172: */
173: public void setLoadByLastAccessTimeQuery(String query) {
174: _loadByLastAccessTimeQuery = query;
175: }
176:
177: public String getLoadByLastAccessTimeQuery() {
178: return _loadByLastAccessTimeQuery;
179: }
180:
181: /**
182: * The SQL Query used to load all sessions whose valid property is equals to the given valid.
183: *
184: * The columns in the result set must be in the following order :
185: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
186: */
187: public void setLoadByValidQuery(String query) {
188: _loadByValidQuery = query;
189: }
190:
191: public String getLoadByValidQuery() {
192: return _loadByValidQuery;
193: }
194:
195: /**
196: * The SQL Query used to load all sessions associated to a given user.
197: *
198: * The columns in the result set must be in the following order :
199: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
200: *
201: * @param query
202: */
203: public void setLoadByUserNameQuery(String query) {
204: _loadByUserNameQuery = query;
205: }
206:
207: public String getLoadByUserNameQuery() {
208: return _loadByUserNameQuery;
209: }
210:
211: /**
212: * The SQL query used to retrieve the number of sessions in the store.
213: * The first column of the first row in the result set must be the number of sessions.
214: */
215: public void setSizeQuery(String query) {
216: _sizeQuery = query;
217: }
218:
219: public String getSizeQuery() {
220: return _sizeQuery;
221: }
222:
223: /**
224: * The SQL Query used to retrieve all session ids.
225: * The first column for each row in the result set must be the session id.
226: */
227: public void setKeysQuery(String query) {
228: _keysQuery = query;
229: }
230:
231: public String getKeysQuery() {
232: return _keysQuery;
233: }
234:
235: /**
236: * The SQL Query used to load all sessions from the store.
237: *
238: * The columns in the result set must be in the following order :
239: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
240: *
241: * @param query
242: */
243: public void setLoadAllQuery(String query) {
244: _loadAllQuery = query;
245: }
246:
247: public String getLoadAllQuery() {
248: return _loadAllQuery;
249: }
250:
251: /**
252: * The SQL Query used to load one session from the store based on its id.
253: *
254: * The columns in the result set must be in the following order :
255: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
256: *
257: * example : SELECT sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid FROM JOSSO_SESSION WHERE sessionId = ?
258: *
259: * @param query
260: */
261: public void setLoadQuery(String query) {
262: _loadQuery = query;
263: }
264:
265: public String getLoadQuery() {
266: return _loadQuery;
267: }
268:
269: // --------------------------------
270: // SessionStore implementation
271: // --------------------------------
272:
273: /**
274: * This method returns the number of stored sessions.
275: *
276: * The first column of the first row in the result set must be the number of sessions.
277: *
278: * @see #setSizeQuery(String)
279: *
280: * @return the number of sessions
281: * @exception SSOSessionException
282: */
283: public int getSize() throws SSOSessionException {
284: int retval = 0;
285:
286: Connection conn = null;
287:
288: // - Submit query
289: // - First column of the first row is the number of sessions.
290: //
291:
292: try {
293: conn = getConnection();
294: final ResultSet rs = conn.createStatement().executeQuery(
295: _sizeQuery);
296: if (rs != null && rs.next()) {
297: retval = rs.getInt(1);
298: }
299:
300: rs.close();
301: } catch (Exception e) {
302: if (__log.isDebugEnabled())
303: __log.debug(e, e);
304: throw new SSOSessionException(e);
305: } finally {
306: close(conn);
307: }
308:
309: if (__log.isDebugEnabled())
310: __log.debug("Returning " + retval);
311:
312: return retval;
313: }
314:
315: /**
316: * Returns all session keys (ids)
317: *
318: * The first column for each row in the result set must be the session id.
319: *
320: * @see #setKeysQuery(String)
321: *
322: * @return The session keys
323: * @exception SSOSessionException
324: */
325: public String[] keys() throws SSOSessionException {
326: String[] retval = null;
327:
328: Connection conn = null;
329:
330: // - Submit query
331: // - First column for each row is the session id.
332: //
333:
334: try {
335: conn = getConnection();
336: final ResultSet rs = conn.createStatement().executeQuery(
337: _keysQuery);
338: final ArrayList bucket = new ArrayList();
339:
340: while (rs.next())
341: bucket.add(rs.getString(1));
342:
343: rs.close();
344:
345: retval = new String[bucket.size()];
346: bucket.toArray(retval);
347: } catch (Exception e) {
348: if (__log.isDebugEnabled())
349: __log.debug(e, e);
350: throw new SSOSessionException(e);
351: } finally {
352: close(conn);
353: }
354:
355: return retval;
356: }
357:
358: /**
359: * Loads all stored sessions.
360: *
361: * The columns in the result set must be in the following order :
362: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
363: *
364: * @see #setLoadAllQuery(String)
365: */
366: public BaseSession[] loadAll() throws SSOSessionException {
367: BaseSession[] retval = null;
368:
369: Connection conn = null;
370:
371: // - Submit query
372: // - Expected columns, in order:
373: // sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
374: //
375:
376: try {
377: conn = getConnection();
378: final ResultSet rs = conn.createStatement().executeQuery(
379: _loadAllQuery);
380: retval = getSessions(rs);
381: rs.close();
382: } catch (Exception e) {
383: if (__log.isDebugEnabled())
384: __log.debug(e, e);
385: throw new SSOSessionException(e);
386: } finally {
387: close(conn);
388: }
389:
390: return retval;
391: }
392:
393: /**
394: * Loads a session based on its id.
395: *
396: * The columns in the result set must be in the following order :
397: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
398: *
399: * @see #setLoadQuery(String)
400: *
401: * @param id
402: * @return
403: * @throws SSOSessionException
404: */
405: public BaseSession load(String id) throws SSOSessionException {
406: BaseSession retval = null;
407: Connection conn = null;
408:
409: try {
410: conn = getConnection();
411: final PreparedStatement ps = conn
412: .prepareStatement(_loadQuery);
413: ps.setString(1, id);
414:
415: final ResultSet rs = ps.executeQuery();
416: if (rs.next())
417: retval = createFromResultSet(rs);
418:
419: rs.close();
420:
421: } catch (Exception e) {
422: if (__log.isDebugEnabled())
423: __log.debug(e, e);
424: throw new SSOSessionException(e);
425: } finally {
426: close(conn);
427: }
428:
429: id = (retval == null) ? "NOT FOUND" : retval.getId();
430:
431: if (__log.isDebugEnabled())
432: __log.debug("Loaded session: " + id);
433:
434: return retval;
435: }
436:
437: /**
438: * Loads all sessions based on the associated username
439: *
440: * The columns in the result set must be in the following order :
441: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
442: *
443: * @see #setLoadByUserNameQuery(String)
444: */
445: public BaseSession[] loadByUsername(String userName)
446: throws SSOSessionException {
447: BaseSession[] retval = null;
448: Connection conn = null;
449:
450: try {
451: conn = getConnection();
452: final PreparedStatement ps = conn
453: .prepareStatement(_loadByUserNameQuery);
454: ps.setString(1, userName);
455:
456: final ResultSet rs = ps.executeQuery();
457: retval = getSessions(rs);
458: rs.close();
459: } catch (Exception e) {
460: if (__log.isDebugEnabled())
461: __log.debug(e, e);
462: throw new SSOSessionException(e);
463: } finally {
464: close(conn);
465: }
466:
467: return retval;
468: }
469:
470: /**
471: * Loads all sessions last accessed before the given date.
472: *
473: * The date is converted to java.sql.Date when setting up the prepared statement.
474: *
475: * The columns in the result set must be in the following order :
476: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
477: *
478: * @see #setLoadByLastAccessTimeQuery(String)
479: */
480: public BaseSession[] loadByLastAccessTime(Date date)
481: throws SSOSessionException {
482: BaseSession[] retval = null;
483: Connection conn = null;
484:
485: try {
486: conn = getConnection();
487: final PreparedStatement ps = conn
488: .prepareStatement(_loadByLastAccessTimeQuery);
489: ps.setLong(1, date.getTime());
490:
491: final ResultSet rs = ps.executeQuery();
492: retval = getSessions(rs);
493:
494: rs.close();
495: } catch (Exception e) {
496: if (__log.isDebugEnabled())
497: __log.debug(e, e);
498: throw new SSOSessionException(e);
499: } finally {
500: close(conn);
501: }
502:
503: return retval;
504: }
505:
506: /**
507: * Loads all sessions whose valid property is equals to the received argument.
508: *
509: * @see #setLoadByValidQuery(String)
510: */
511: public BaseSession[] loadByValid(boolean valid)
512: throws SSOSessionException {
513: BaseSession[] retval = null;
514: Connection conn = null;
515:
516: try {
517: conn = getConnection();
518: final PreparedStatement ps = conn
519: .prepareStatement(_loadByValidQuery);
520: ps.setBoolean(1, valid);
521:
522: final ResultSet rs = ps.executeQuery();
523: retval = getSessions(rs);
524:
525: rs.close();
526: } catch (Exception e) {
527: if (__log.isDebugEnabled())
528: __log.debug(e, e);
529: throw new SSOSessionException(e);
530: } finally {
531: close(conn);
532: }
533:
534: return retval;
535: }
536:
537: /**
538: * Removes a session from the store based on its id
539: *
540: * @see #setDeleteDml(String)
541: *
542: * @exception SSOSessionException
543: */
544: public void remove(String id) throws SSOSessionException {
545: Connection conn = null;
546:
547: try {
548: conn = getConnection();
549: delete(conn, id);
550: conn.commit();
551: } catch (Exception e) {
552: if (__log.isDebugEnabled())
553: __log.debug(e, e);
554: throw new SSOSessionException(e);
555: } finally {
556: close(conn);
557: }
558: }
559:
560: /**
561: * Removes ALL stored sessions
562: *
563: * @see #setDeleteAllDml(String)
564: *
565: * @throws SSOSessionException
566: */
567: public void clear() throws SSOSessionException {
568: Connection conn = null;
569:
570: try {
571: conn = getConnection();
572: final Statement stmt = conn.createStatement();
573: stmt.execute(_deleteAllDml);
574: conn.commit();
575:
576: stmt.close();
577: } catch (Exception e) {
578: if (__log.isDebugEnabled())
579: __log.debug(e, e);
580:
581: throw new SSOSessionException(e);
582: } finally {
583: close(conn);
584: }
585: }
586:
587: /**
588: * Stores a session in the DB. This method opens a transaccion, removes the old session if present, the creates
589: * it again using the configured savenDml Query and commits the transaction.
590: *
591: * Session attributes will be passed to the prepared statemetn in the following order :
592: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
593: *
594: * @see #setInsertDml(String)
595: *
596: * @param session
597: * @throws SSOSessionException
598: */
599: public void save(BaseSession session) throws SSOSessionException {
600: Connection conn = null;
601:
602: // - Expected columns, in order:
603: // sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
604:
605: try {
606: conn = getConnection();
607: delete(conn, session.getId());
608: insert(conn, session);
609: conn.commit();
610: if (__log.isDebugEnabled())
611: __log.debug("Session committed: " + session.getId());
612: } catch (Exception e) {
613: if (__log.isDebugEnabled())
614: __log.debug(e, e);
615:
616: if (conn != null) {
617:
618: try {
619: conn.rollback();
620: } catch (SQLException sqle) {
621: if (__log.isDebugEnabled())
622: __log.debug("Error during ROLLBACK ", sqle);
623: }
624: }
625:
626: throw new SSOSessionException(e);
627: } finally {
628: close(conn);
629: }
630:
631: if (__log.isDebugEnabled())
632: __log.debug("Saved session: " + session.getId());
633: }
634:
635: // ---------------------------
636: // Private Methods
637: // ---------------------------
638:
639: /**
640: * This removes a session, using the value of the removeDml property as prepared statement.
641: *
642: * @param conn
643: * @param sessionId
644: * @throws SQLException
645: */
646: protected void delete(Connection conn, String sessionId)
647: throws SQLException {
648: final PreparedStatement ps = conn.prepareStatement(_deleteDml);
649: ps.setString(1, sessionId);
650: ps.execute();
651:
652: if (__log.isDebugEnabled())
653: __log.debug("Session Removed: " + sessionId);
654: }
655:
656: protected void insert(Connection conn, BaseSession session)
657: throws SQLException {
658: final PreparedStatement ps = conn.prepareStatement(_insertDml);
659: ps.setString(1, session.getId());
660: ps.setString(2, session.getUsername());
661: ps.setLong(3, session.getCreationTime());
662: ps.setLong(4, session.getLastAccessTime());
663: ps.setInt(5, (int) session.getAccessCount());
664: ps.setInt(6, session.getMaxInactiveInterval());
665: ps.setBoolean(7, session.isValid());
666: ps.execute();
667:
668: if (__log.isDebugEnabled())
669: __log.debug("Creation, LastAccess: "
670: + session.getCreationTime() + ", "
671: + session.getCreationTime());
672:
673: if (__log.isDebugEnabled())
674: __log.debug("Session inserted: " + session.getId());
675: }
676:
677: /**
678: *
679: */
680: protected BaseSession[] getSessions(ResultSet rs)
681: throws SQLException {
682: final ArrayList bucket = new ArrayList();
683:
684: // - Collect the sessions
685: // - Return the sessions in an array.
686: //
687:
688: while (rs.next())
689: bucket.add(createFromResultSet(rs));
690:
691: final BaseSession[] retval = new BaseSession[bucket.size()];
692: bucket.toArray(retval);
693:
694: return retval;
695: }
696:
697: /**
698: * This method builds a session instance based on a result set.
699: *
700: * Expected columns, in order:
701: * sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
702: *
703: * @param rs
704: * @return
705: * @throws SQLException
706: */
707: protected BaseSession createFromResultSet(ResultSet rs)
708: throws SQLException {
709: // - Expected columns, in order:
710: // sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
711:
712: final MutableBaseSession bsi = new MutableBaseSession();
713: bsi.setId(rs.getString(1));
714: bsi.setUsername(rs.getString(2));
715: bsi.setCreationTime(rs.getLong(3));
716: bsi.setLastAccessedTime(rs.getLong(4));
717: bsi.setAccessCount(rs.getLong(5));
718: bsi.setMaxInactiveInterval(rs.getInt(6));
719: bsi.setValid(rs.getBoolean(7));
720:
721: return bsi;
722: }
723: }
|