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
006 package fc.jdbc;
007
008 import java.io.*;
009 import java.util.*;
010 import java.sql.*;
011
012 import fc.io.*;
013 import fc.util.*;
014
015 /**
016 This class implements the gateway to a SQL Database. It should be
017 used to request connections.
018
019 @author hursh jain
020 */
021 public abstract class ConnectionMgr
022 {
023 final protected Log log; //for all logging messages
024 String myname = getClass().getName();
025
026 private volatile boolean mgrClosed = false;
027 //== jdbc ====================================
028 protected volatile String catalogName;
029 protected String url;
030 protected String driver;
031 protected String user;
032 protected String password;
033 protected Driver mydriver;
034 protected DBName dbname;
035
036 private 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 /**
058 Creates a new ConnectionMgr with logging to
059 logging 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 */
068 public 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 /**
082 Delegates to {@link ConnectionMgr(Log, PropertyMgr)} with logging to {@link fc.io.Log#getDefault()}.
083 */
084 public ConnectionMgr(PropertyMgr props)
085 throws Exception
086 {
087 this(Log.getDefault(), props, null);
088 }
089
090 /**
091 Delegates to {@link ConnectionMgr(Log, PropertyMgr)} with logging to {@link fc.io.Log#getDefault()}
092 and using the specified <tt>prefix</tt> for property names.
093 */
094 public ConnectionMgr(PropertyMgr props, String prefix)
095 throws Exception
096 {
097 this(Log.getDefault(), props, prefix);
098 }
099
100 /**
101 Constructs a new ConnectionMgr. The default implementation does not use a
102 connection pool but creates new Connections, whenever needed. Expects the
103 following properties, which should accessible via the specified {@link
104 fc.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>
113 Throws an Exception if this object cannot be constructed for some reason.
114 **/
115 public ConnectionMgr(Log log, PropertyMgr props)
116 throws Exception
117 {
118 this(log, props, null);
119 }
120
121
122 /**
123 Constructs a new ConnectionMgr. The default implementation does not use a
124 connection pool but creates new Connections, whenever needed. Expects the
125 following properties, which should accessible via the specified {@link
126 fc.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>
135 The optional prefix parameter (if not null) is prepended to each property name
136 before it is accessed from the property manager.
137 For example:
138 <blockquote><tt>jdbc.url</tt></blockquote>
139 becomes:
140 <blockquote><i>prefix</i><tt>jdbc.url</tt></blockquote>
141
142 Throws an Exception if this object cannot be constructed for some reason.
143 **/
144 public ConnectionMgr(Log log, PropertyMgr props, String prefix)
145 throws 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 /**
178 Returns the {@link java.sql.Driver} that this connection manager
179 is using to connect to the database.
180 **/
181 public Driver getDriver() {
182 return mydriver;
183 }
184
185 /**
186 Returns the dbname corresponding that the database connected to by this
187 connection manager. Useful for writing database specific code as/when
188 applicable.
189 */
190 public DBName getDBName() throws SQLException
191 {
192 return dbname;
193 }
194
195 /**
196 If set, the ConnectionMgr will always return connections
197 set to the specified catalog. Set this to <tt>null</tt> to
198 return connections not set to any catalog.
199 **/
200 public void setCatalog(String name) {
201 this.catalogName = name;
202 }
203
204 /**
205 Returns a connection if successful, otherwise throws a
206 <tt>SQLException</tt>. The returned has the default JDBC
207 properties, including being in auto-commit mode (which means
208 that it automatically commits changes after executing each
209 statement).
210 **/
211 public 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 /**
227 Closes the ConnectionMgr. After this call, no more connections can be checked
228 out and any existing checked out connections are closed. Does <b>not</b> throw
229 an Exception but returns false (instead) if the ConnectionMgr could not
230 be closed. Calls the {@link #handleMgrShutdown()} method inside a
231 synchronized block.
232
233 @return true if connection manager closed successfully, false otherwise
234 **/
235 synchronized 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, **/
245 public String getURL() {
246 return url;
247 }
248
249 public String toString() {
250 return myname;
251 }
252
253 //== Abstract methods ======================================
254
255 /**
256 Is called by the {@link #close()} method. By default does nothing but
257 subclasses can override this method to handle connection manager
258 shutdown as deemed necessary.
259 @return true if handler succeeded, false otherwise.
260 **/
261 protected abstract boolean handleMgrShutdown();
262
263 /**
264 Returns a connection to the DB everytime it's called.
265 **/
266 protected abstract Connection getConImpl() throws SQLException;
267
268 } //~class ConnectionMgr
269
270