001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.jdbc; 007 008import java.io.*; 009import java.util.*; 010import java.sql.*; 011 012import fc.io.*; 013import fc.util.*; 014 015/** 016This class implements the gateway to a SQL Database. It should be 017used to request connections. 018 019@author hursh jain 020*/ 021public abstract class ConnectionMgr 022{ 023final protected Log log; //for all logging messages 024 String myname = getClass().getName(); 025 026private volatile boolean mgrClosed = false; 027//== jdbc ==================================== 028protected volatile String catalogName; 029protected String url; 030protected String driver; 031protected String user; 032protected String password; 033protected Driver mydriver; 034protected DBName dbname; 035 036private static class PropertyMgrWrapper extends PropertyMgr 037 { 038 Map map = new HashMap(); 039 PropertyMgrWrapper( 040 String url, String driver, 041 String user, String password, String catalog) 042 { 043 map.put("jdbc.url", url); 044 map.put("jdbc.driver", driver); 045 map.put("jdbc.user", user); 046 map.put("jdbc.password", password); 047 map.put("jdbc.catalog", catalog); 048 } 049 050 public String get(String name) { 051 return (String) map.get(name); 052 } 053 public String set(String name, String backup) { Log.getDefault().error("not implemented"); return null; } 054 public void save() { Log.getDefault().error("not implemented");} 055 } 056 057/** 058Creates a new ConnectionMgr with logging to 059logging to {@link fc.io.Log#getDefault()}. 060 061@param jdbc.url 062@param jdbc.driver 063@param jdbc.user 064@param jdbc.password 065@param jdbc.catalog optional, sets the default and can be 066<tt>null</tt> 067*/ 068public ConnectionMgr( 069 String jdbc_url, String jdbc_driver, 070 String jdbc_user, String jdbc_password, 071 String jdbc_catalog) 072 throws Exception 073 { 074 //we need to delegate because other constructrs do 075 //things like driver initialization etc. 076 this(new PropertyMgrWrapper( 077 jdbc_url, jdbc_driver, jdbc_user, jdbc_password, 078 jdbc_catalog)); 079 } 080 081/** 082Delegates to {@link ConnectionMgr(Log, PropertyMgr)} with logging to {@link fc.io.Log#getDefault()}. 083*/ 084public ConnectionMgr(PropertyMgr props) 085throws Exception 086 { 087 this(Log.getDefault(), props, null); 088 } 089 090/** 091Delegates to {@link ConnectionMgr(Log, PropertyMgr)} with logging to {@link fc.io.Log#getDefault()} 092and using the specified <tt>prefix</tt> for property names. 093*/ 094public ConnectionMgr(PropertyMgr props, String prefix) 095throws Exception 096 { 097 this(Log.getDefault(), props, prefix); 098 } 099 100/** 101Constructs a new ConnectionMgr. The default implementation does not use a 102connection pool but creates new Connections, whenever needed. Expects the 103following properties, which should accessible via the specified {@link 104fc.app.PropertyMgr}. 105<ol> 106 <li><tt>jdbc.url</tt></li> 107 <li><tt>jdbc.driver</tt></li> 108 <li><tt>jdbc.user</tt></li> 109 <li><tt>jdbc.password</tt></li> 110 <li>An optional <tt>jdbc.catalog</tt> property sets the 111 default jdbc catalog. Can be <tt>null</tt>.</li> 112</ol> 113Throws an Exception if this object cannot be constructed for some reason. 114**/ 115public ConnectionMgr(Log log, PropertyMgr props) 116throws Exception 117 { 118 this(log, props, null); 119 } 120 121 122/** 123Constructs a new ConnectionMgr. The default implementation does not use a 124connection pool but creates new Connections, whenever needed. Expects the 125following properties, which should accessible via the specified {@link 126fc.app.PropertyMgr}. 127<ol> 128 <li><tt>jdbc.url</tt></li> 129 <li><tt>jdbc.driver</tt></li> 130 <li><tt>jdbc.user</tt></li> 131 <li><tt>jdbc.password</tt></li> 132 <li>An optional <tt>jdbc.catalog</tt> property sets the 133 default jdbc catalog. Can be <tt>null</tt>.</li> 134</ol> 135The optional prefix parameter (if not null) is prepended to each property name 136before it is accessed from the property manager. 137For example: 138 <blockquote><tt>jdbc.url</tt></blockquote> 139becomes: 140 <blockquote><i>prefix</i><tt>jdbc.url</tt></blockquote> 141 142Throws an Exception if this object cannot be constructed for some reason. 143**/ 144public ConnectionMgr(Log log, PropertyMgr props, String prefix) 145throws Exception 146 { 147 //1. don't call toString() from this constructor since it's 148 //overriden in subclasses 149 150 Argcheck.notnull(log); 151 Argcheck.notnull(props); 152 153 this.log = log; 154 155 url = props.getRequired(prefix!=null?prefix+"jdbc.url":"jdbc.url"); 156 driver = props.getRequired(prefix!=null?prefix+"jdbc.driver":"jdbc.driver"); 157 user = props.getRequired(prefix!=null?prefix+"jdbc.user":"jdbc.user"); 158 password = props.getRequired(prefix!=null?prefix+"jdbc.password":"jdbc.password"); 159 catalogName = props.get(prefix!=null?prefix+"jdbc.catalog":"jdbc.catalog"); //can be null; 160 161 // Register driver, Use normal instantiation and manually register the 162 //driver - does not rely on static initializer being run. 163 final Class mydriver_class = Class.forName(driver); 164 mydriver = (Driver) mydriver_class.newInstance(); 165 DriverManager.registerDriver(mydriver); 166 167 dbname = DBName.fromDriver(mydriver_class.getName()); 168 if (dbname == null) { 169 log.warn("Cannot guess dbname from the driver. Connections will still work perfectly but getDBName() in this class will return null"); 170 } 171 172 String sep = System.getProperty("line.separator"); 173 log.bug("ConnectionMgr constructor; driver="+driver+sep+"url="+url+sep+"user="+user+sep+"password="+password+sep+"catalog="+catalogName); 174 } 175 176 177/** 178Returns the {@link java.sql.Driver} that this connection manager 179is using to connect to the database. 180**/ 181public Driver getDriver() { 182 return mydriver; 183 } 184 185/** 186Returns the dbname corresponding that the database connected to by this 187connection manager. Useful for writing database specific code as/when 188applicable. 189*/ 190public DBName getDBName() throws SQLException 191 { 192 return dbname; 193 } 194 195/** 196If set, the ConnectionMgr will always return connections 197set to the specified catalog. Set this to <tt>null</tt> to 198return connections not set to any catalog. 199**/ 200public void setCatalog(String name) { 201 this.catalogName = name; 202 } 203 204/** 205Returns a connection if successful, otherwise throws a 206<tt>SQLException</tt>. The returned has the default JDBC 207properties, including being in auto-commit mode (which means 208that it automatically commits changes after executing each 209statement). 210**/ 211public Connection getConnection() throws SQLException 212 { 213 if (mgrClosed) { 214 log.warn(this, "tried to get a connection from closed ConnectionMgr"); 215 throw new SQLException("tried to get a connection from closed ConnectionMgr"); 216 } 217 Connection con = getConImpl(); 218 if (catalogName != null) { 219 log.bug(this, "setting catalog name to: ", catalogName); 220 con.setCatalog(catalogName); 221 } 222 log.bug(this, ", returning connection=", con); 223 return con; 224 } 225 226/** 227Closes the ConnectionMgr. After this call, no more connections can be checked 228out and any existing checked out connections are closed. Does <b>not</b> throw 229an Exception but returns false (instead) if the ConnectionMgr could not 230be closed. Calls the {@link #handleMgrShutdown()} method inside a 231synchronized block. 232 233@return true if connection manager closed successfully, false otherwise 234**/ 235synchronized public boolean close() 236 { 237 synchronized (this) { 238 mgrClosed = true; 239 log.bug(this, ": closing ConnectionMgr"); 240 return handleMgrShutdown(); 241 } 242 } 243 244/** Returns the jdbc url that this connection manager is using, **/ 245public String getURL() { 246 return url; 247 } 248 249public String toString() { 250 return myname; 251 } 252 253//== Abstract methods ====================================== 254 255/** 256Is called by the {@link #close()} method. By default does nothing but 257subclasses can override this method to handle connection manager 258shutdown as deemed necessary. 259@return true if handler succeeded, false otherwise. 260**/ 261protected abstract boolean handleMgrShutdown(); 262 263/** 264Returns a connection to the DB everytime it's called. 265**/ 266protected abstract Connection getConImpl() throws SQLException; 267 268} //~class ConnectionMgr 269 270