001: package net.sourceforge.squirrel_sql.client.session;
002:
003: /*
004: * Copyright (C) 2004 Colin Bell
005: * colbell@users.sourceforge.net
006: *
007: * Modifications Copyright (C) 2003-2004 Jason Height
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: */
023: import java.sql.SQLException;
024: import java.util.ArrayList;
025: import java.util.Collections;
026: import java.util.HashMap;
027: import java.util.Hashtable;
028: import java.util.LinkedList;
029: import java.util.Map;
030:
031: import javax.swing.SwingUtilities;
032: import javax.swing.event.EventListenerList;
033:
034: import net.sourceforge.squirrel_sql.client.IApplication;
035: import net.sourceforge.squirrel_sql.client.gui.db.SQLAlias;
036: import net.sourceforge.squirrel_sql.client.gui.db.ISQLAliasExt;
037: import net.sourceforge.squirrel_sql.client.session.event.ISessionListener;
038: import net.sourceforge.squirrel_sql.client.session.event.SessionEvent;
039: import net.sourceforge.squirrel_sql.fw.gui.Dialogs;
040: import net.sourceforge.squirrel_sql.fw.id.IIdentifier;
041: import net.sourceforge.squirrel_sql.fw.id.IntegerIdentifierFactory;
042: import net.sourceforge.squirrel_sql.fw.sql.ISQLConnection;
043: import net.sourceforge.squirrel_sql.fw.sql.ISQLDriver;
044: import net.sourceforge.squirrel_sql.fw.sql.SQLConnection;
045: import net.sourceforge.squirrel_sql.fw.util.StringManager;
046: import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
047: import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
048: import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
049:
050: /**
051: * This class manages sessions.
052: *
053: * @author <A HREF="mailto:colbell@users.sourceforge.net">Colin Bell</A>
054: */
055: public class SessionManager {
056: /** Logger for this class. */
057: private static final ILogger s_log = LoggerController
058: .createLogger(SessionManager.class);
059:
060: /** Internationalized strings for this class. */
061: private static final StringManager s_stringMgr = StringManagerFactory
062: .getStringManager(SessionManager.class);
063:
064: /** Application API. */
065: private final IApplication _app;
066:
067: private ISession _activeSession;
068:
069: /** Linked list of sessions. */
070: private final LinkedList<ISession> _sessionsList = new LinkedList<ISession>();
071:
072: /** Map of sessions keyed by session ID. */
073: private final Map<IIdentifier, ISession> _sessionsById = new HashMap<IIdentifier, ISession>();
074:
075: private EventListenerList listenerList = new EventListenerList();
076:
077: /** Factory used to generate session IDs. */
078: private final IntegerIdentifierFactory _idFactory = new IntegerIdentifierFactory(
079: 1);
080: private ArrayList<IAllowedSchemaChecker> _allowedSchemaCheckers = new ArrayList<IAllowedSchemaChecker>();
081: private Hashtable<IIdentifier, String[]> _allowedSchemasBySessionID = new Hashtable<IIdentifier, String[]>();
082:
083: /**
084: * Ctor.
085: *
086: * @param app Application API.
087: *
088: * @throws IllegalArgumentException
089: * Thrown if <TT>null</TT> <TT>IApplication</TT> passed.
090: */
091: public SessionManager(IApplication app) {
092: super ();
093: if (app == null) {
094: throw new IllegalArgumentException("IApplication == null");
095: }
096:
097: _app = app;
098: }
099:
100: /**
101: * Create a new session.
102: *
103: * @param app Application API.
104: * @param driver JDBC driver for session.
105: * @param alias Defines URL to database.
106: * @param conn Connection to database.
107: * @param user User name connected with.
108: * @param password Password for <TT>user</TT>
109: *
110: * @throws IllegalArgumentException
111: * Thrown if IApplication, ISQLDriver, ISQLAlias,
112: * or SQLConnection is passed as null.
113: */
114: public synchronized ISession createSession(IApplication app,
115: ISQLDriver driver, SQLAlias alias, SQLConnection conn,
116: String user, String password) {
117: if (app == null) {
118: throw new IllegalArgumentException(
119: "null IApplication passed");
120: }
121: if (driver == null) {
122: throw new IllegalArgumentException("null ISQLDriver passed");
123: }
124: if (alias == null) {
125: throw new IllegalArgumentException("null ISQLAlias passed");
126: }
127: if (conn == null) {
128: throw new IllegalArgumentException(
129: "null SQLConnection passed");
130: }
131:
132: final Session sess = new Session(app, driver, alias, conn,
133: user, password, _idFactory.createIdentifier());
134: _sessionsList.addLast(sess);
135: _sessionsById.put(sess.getIdentifier(), sess);
136:
137: fireSessionAdded(sess);
138: setActiveSession(sess);
139:
140: return sess;
141: }
142:
143: public void setActiveSession(ISession session) {
144: if (session != _activeSession) {
145: _activeSession = session;
146: fireSessionActivated(session);
147: }
148: }
149:
150: /**
151: * Retrieve an array of all the sessions currently connected.
152: *
153: * @return array of all connected sessions.
154: */
155: public synchronized ISession[] getConnectedSessions() {
156: return _sessionsList
157: .toArray(new ISession[_sessionsList.size()]);
158: }
159:
160: /**
161: * Retrieve the session that is currently activated within the
162: * session manager. Any new sql worksheets etc will be created
163: * against this session
164: */
165: public synchronized ISession getActiveSession() {
166: return _activeSession;
167: }
168:
169: /**
170: * Get the next session opened after the passed one.
171: *
172: * @return The next session or the first one if the passed one is
173: * the last session.
174: */
175: public synchronized ISession getNextSession(ISession session) {
176: final int sessionCount = _sessionsList.size();
177: int idx = _sessionsList.indexOf(session);
178: if (idx != -1) {
179: ++idx;
180: if (idx >= sessionCount) {
181: idx = 0;
182: }
183: return _sessionsList.get(idx);
184: }
185:
186: s_log.error("SessionManager.getNextSession()-> Session "
187: + session.getIdentifier()
188: + " not found in _sessionsList");
189: if (sessionCount > 0) {
190: s_log
191: .error("SessionManager.getNextSession()-> Returning first session");
192: return _sessionsList.getFirst();
193: }
194: s_log
195: .error("SessionManager.getNextSession()-> List empty so returning passed session");
196: return session;
197: }
198:
199: /**
200: * Get the next session opened before the passed one.
201: *
202: * @return The previous session or the last one if the passed one is
203: * the first session.
204: */
205: public synchronized ISession getPreviousSession(ISession session) {
206: final int sessionCount = _sessionsList.size();
207: int idx = _sessionsList.indexOf(session);
208: if (idx != -1) {
209: --idx;
210: if (idx < 0) {
211: idx = sessionCount - 1;
212: }
213: return _sessionsList.get(idx);
214: }
215:
216: s_log.error("SessionManager.getPreviousSession()-> Session "
217: + session.getIdentifier()
218: + " not found in _sessionsList");
219: if (sessionCount > 0) {
220: s_log
221: .error("SessionManager.getPreviousSession()-> Returning last session");
222: return _sessionsList.getLast();
223: }
224: s_log
225: .error("SessionManager.getPreviousSession()-> List empty so returning passed session");
226: return session;
227: }
228:
229: /**
230: * Retrieve the session for the passed identifier.
231: *
232: * @param sessionID ID of session we are trying to retrieve.
233: *
234: * @throws IllegalArgumentException
235: * Thrown if <TT>null</TT> <TT>IIdentifier</TT> passed.
236: */
237: public ISession getSession(IIdentifier sessionID) {
238: return _sessionsById.get(sessionID);
239: }
240:
241: /**
242: * Close a session.
243: *
244: * @param session Session to close.
245: *
246: * @return <tt>true</tt> if session was closed else <tt>false</tt>.
247: *
248: * @throws IllegalArgumentException
249: * Thrown if <TT>null</TT>ISession passed.
250: */
251: public synchronized boolean closeSession(ISession session) {
252: if (session == null) {
253: throw new IllegalArgumentException("ISession == null");
254: }
255:
256: try {
257: if (confirmClose(session)) {
258: // TODO: Should have session listeners instead of these calls.
259: session.getApplication().getPluginManager()
260: .sessionEnding(session);
261:
262: fireSessionClosing(session);
263: try {
264: session.close();
265: } catch (SQLException sqle) {
266: s_log.error("Error closing Session", sqle);
267: session
268: .showErrorMessage(s_stringMgr
269: .getString(
270: "SessionManager.ErrorClosingSession",
271: sqle));
272: }
273: fireSessionClosed(session);
274:
275: final IIdentifier sessionId = session.getIdentifier();
276: if (!_sessionsList.remove(session)) {
277: s_log
278: .error("SessionManager.closeSession()-> Session "
279: + sessionId
280: + " not found in _sessionsList when trying to remove it.");
281: }
282: if (_sessionsById.remove(sessionId) == null) {
283: s_log
284: .error("SessionManager.closeSession()-> Session "
285: + sessionId
286: + " not found in _sessionsById when trying to remove it.");
287: }
288:
289: if (_sessionsList.isEmpty()) {
290: fireAllSessionsClosed();
291: }
292:
293: // Activate another session since the current
294: // active session has closed.
295: if (session == _activeSession) {
296: if (!_sessionsList.isEmpty()) {
297: setActiveSession(_sessionsList.getLast());
298: } else {
299: _activeSession = null;
300: }
301: }
302:
303: _allowedSchemasBySessionID.remove(session
304: .getIdentifier());
305:
306: return true;
307: }
308: } catch (Throwable ex) {
309: s_log.error("Error closing Session", ex);
310: session.showErrorMessage(s_stringMgr.getString(
311: "SessionManager.ErrorClosingSession", ex));
312: }
313:
314: return false;
315: }
316:
317: /**
318: * Closes all currently open sessions.
319: *
320: * @return <tt>true</tt> if all sessions closed else <tt>false</tt>.
321: *
322: * @throws SQLException
323: * Thrown if an error closing the SQL connection. The session
324: * will still be closed even though the connection may not have
325: * been.
326: */
327: synchronized public boolean closeAllSessions() {
328: // Get an array since we dont want trouble with the sessionsList when
329: // we remove the sessions from it.
330: final ISession[] sessions = getConnectedSessions();
331: for (int i = sessions.length - 1; i >= 0; i--) {
332: if (!closeSession(sessions[i])) {
333: return false;
334: }
335: }
336: return true;
337: }
338:
339: /**
340: * Adds a session listener
341: *
342: * @param lis The listener to add.
343: */
344: public void addSessionListener(ISessionListener lis) {
345: if (lis != null) {
346: listenerList.add(ISessionListener.class, lis);
347: } else {
348: s_log
349: .error("Attempted to add null listener: SessionManager.addSessionListener");
350: }
351: }
352:
353: /**
354: * Removes a session listener
355: *
356: * @param lis The listener to remove.
357: */
358: public void removeSessionListener(ISessionListener lis) {
359: if (lis != null) {
360: listenerList.remove(ISessionListener.class, lis);
361: } else {
362: s_log
363: .error("Attempted to remove null listener: SessionManager.addSessionListener");
364: }
365: }
366:
367: /**
368: * Fired when a session is connected (added) to the session
369: * manager
370: */
371: protected void fireSessionAdded(ISession session) {
372: Object[] listeners = listenerList.getListenerList();
373: SessionEvent evt = null;
374: for (int i = listeners.length - 2; i >= 0; i -= 2) {
375: if (listeners[i] == ISessionListener.class) {
376: // Lazily create the event:
377: if (evt == null)
378: evt = new SessionEvent(session);
379: ((ISessionListener) listeners[i + 1])
380: .sessionConnected(evt);
381: }
382: }
383: }
384:
385: /**
386: * Fired when a session is closed (removed) from the session manager
387: */
388: protected void fireSessionClosed(ISession session) {
389: Object[] listeners = listenerList.getListenerList();
390: SessionEvent evt = null;
391: for (int i = listeners.length - 2; i >= 0; i -= 2) {
392: if (listeners[i] == ISessionListener.class) {
393: // Lazily create the event:
394: if (evt == null)
395: evt = new SessionEvent(session);
396: ((ISessionListener) listeners[i + 1])
397: .sessionClosed(evt);
398: }
399: }
400: }
401:
402: /**
403: * Fired when a session is about to close from the session manager
404: */
405: protected void fireSessionClosing(ISession session) {
406: Object[] listeners = listenerList.getListenerList();
407: SessionEvent evt = null;
408: for (int i = listeners.length - 2; i >= 0; i -= 2) {
409: if (listeners[i] == ISessionListener.class) {
410: // Lazily create the event:
411: if (evt == null) {
412: evt = new SessionEvent(session);
413: }
414: ((ISessionListener) listeners[i + 1])
415: .sessionClosing(evt);
416: }
417: }
418: }
419:
420: /**
421: * Fired when all the session have been closed (removed) from the
422: * session manager
423: */
424: protected void fireAllSessionsClosed() {
425: Object[] listeners = listenerList.getListenerList();
426: for (int i = listeners.length - 2; i >= 0; i -= 2) {
427: if (listeners[i] == ISessionListener.class) {
428: ((ISessionListener) listeners[i + 1])
429: .allSessionsClosed();
430: }
431: }
432: }
433:
434: /**
435: * Fired when the active session changed
436: */
437: protected void fireSessionActivated(ISession session) {
438: Object[] listeners = listenerList.getListenerList();
439: SessionEvent evt = null;
440: for (int i = listeners.length - 2; i >= 0; i -= 2) {
441: if (listeners[i] == ISessionListener.class) {
442: // Lazily create the event:
443: if (evt == null)
444: evt = new SessionEvent(session);
445: ((ISessionListener) listeners[i + 1])
446: .sessionActivated(evt);
447: }
448: }
449: }
450:
451: /**
452: * Confirm whether session is to be closed.
453: *
454: * @param session Session being closed.
455: *
456: * @return <tt>true</tt> if confirmed to close session.
457: */
458: private boolean confirmClose(ISession session) {
459: if (!_app.getSquirrelPreferences().getConfirmSessionClose()) {
460: return session.confirmClose();
461: }
462:
463: final String msg = s_stringMgr.getString(
464: "SessionManager.confirmClose", session.getTitle());
465: if (!Dialogs.showYesNo(_app.getMainFrame(), msg)) {
466: return false;
467: } else {
468: return session.confirmClose();
469: }
470: }
471:
472: protected void fireConnectionClosedForReconnect(Session session) {
473: Object[] listeners = listenerList.getListenerList();
474: SessionEvent evt = null;
475: for (int i = listeners.length - 2; i >= 0; i -= 2) {
476: if (listeners[i] == ISessionListener.class) {
477: // Lazily create the event:
478: if (evt == null)
479: evt = new SessionEvent(session);
480: ((ISessionListener) listeners[i + 1])
481: .connectionClosedForReconnect(evt);
482: }
483: }
484: }
485:
486: protected void fireReconnected(Session session) {
487: Object[] listeners = listenerList.getListenerList();
488: SessionEvent evt = null;
489: for (int i = listeners.length - 2; i >= 0; i -= 2) {
490: if (listeners[i] == ISessionListener.class) {
491: // Lazily create the event:
492: if (evt == null)
493: evt = new SessionEvent(session);
494: ((ISessionListener) listeners[i + 1]).reconnected(evt);
495: }
496: }
497: }
498:
499: protected void fireReconnectFailed(Session session) {
500: Object[] listeners = listenerList.getListenerList();
501: SessionEvent evt = null;
502: for (int i = listeners.length - 2; i >= 0; i -= 2) {
503: if (listeners[i] == ISessionListener.class) {
504: // Lazily create the event:
505: if (evt == null)
506: evt = new SessionEvent(session);
507: ((ISessionListener) listeners[i + 1])
508: .reconnectFailed(evt);
509: }
510: }
511: }
512:
513: protected void fireSessionFinalized(
514: final IIdentifier sessionIdentifier) {
515: // invokeLater to make the call synchronto the event queue
516: SwingUtilities.invokeLater(new Runnable() {
517: public void run() {
518: Object[] listeners = listenerList.getListenerList();
519: for (int i = listeners.length - 2; i >= 0; i -= 2) {
520: if (listeners[i] == ISessionListener.class) {
521: ((ISessionListener) listeners[i + 1])
522: .sessionFinalized(sessionIdentifier);
523: }
524: }
525: }
526: });
527:
528: }
529:
530: public void addAllowedSchemaChecker(
531: IAllowedSchemaChecker allowedSchemaChecker) {
532: _allowedSchemaCheckers.add(allowedSchemaChecker);
533: }
534:
535: public boolean areAllSchemasAllowed(ISession session) {
536: try {
537: String[] allowedSchemas = getAllowedSchemas(session);
538: String[] schemas = session.getSQLConnection()
539: .getSQLMetaData().getSchemas();
540:
541: return allowedSchemas.length == schemas.length;
542: } catch (SQLException e) {
543: s_log.error("Failed to check allowed Schemas", e);
544: return true;
545: }
546: }
547:
548: public String[] getAllowedSchemas(ISession session) {
549: String[] allowedSchemas = _allowedSchemasBySessionID
550: .get(session.getIdentifier());
551: if (null == allowedSchemas) {
552: allowedSchemas = getAllowedSchemas(session
553: .getSQLConnection(), session.getAlias());
554: _allowedSchemasBySessionID.put(session.getIdentifier(),
555: allowedSchemas);
556: }
557:
558: return allowedSchemas;
559:
560: }
561:
562: /**
563: * Note: This Method does not cache allowed Schemas.
564: * It is preferable to use getAllowedSchemas(ISession) if a Session is avaialable.
565: */
566: public String[] getAllowedSchemas(ISQLConnection con,
567: ISQLAliasExt alias) {
568: try {
569: // Do not do new HashMap() here.
570: HashMap<String, Object> uniqueAllowedSchemas = null;
571:
572: for (int i = 0; i < _allowedSchemaCheckers.size(); i++) {
573: String[] allowedSchemas = null;
574: try {
575: allowedSchemas = (_allowedSchemaCheckers.get(i))
576: .getAllowedSchemas(con, alias);
577: } catch (Exception e) {
578: s_log
579: .error(
580: "Failed to get allowed Schemas from Plugin",
581: e);
582: }
583:
584: if (null != allowedSchemas) {
585: if (null == uniqueAllowedSchemas) {
586: uniqueAllowedSchemas = new HashMap<String, Object>();
587: }
588:
589: for (int j = 0; j < allowedSchemas.length; j++) {
590: uniqueAllowedSchemas.put(allowedSchemas[j],
591: null);
592: }
593: }
594: }
595:
596: if (null == uniqueAllowedSchemas) {
597: return con.getSQLMetaData().getSchemas();
598: } else {
599: ArrayList<String> list = new ArrayList<String>(
600: uniqueAllowedSchemas.keySet());
601: Collections.sort(list);
602: return list.toArray(new String[list.size()]);
603: }
604: } catch (Exception e) {
605: s_log.error("Failed to get allowed Schemas", e);
606: return new String[0];
607: }
608: }
609:
610: public void clearAllowedSchemaCache(ISession session) {
611: _allowedSchemasBySessionID.remove(session.getIdentifier());
612: }
613: }
|