Source code: org/enableit/db/ConnectionFactory.java
1 /*
2 * PROJECT : __PROJECT_NAME__
3 *
4 * COPYRIGHT : Copyright (C) 1999,2000,2001,2002 enableIT.org
5 *
6 * contact us at gpl@enableit.org.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22 package org.enableit.db ;
23
24 // Java Imports
25 import java.io.InputStream ;
26 import java.io.IOException ;
27 import java.net.URL ;
28 import java.sql.Connection ;
29 import java.sql.DriverManager ;
30 import java.sql.SQLException ;
31 import java.util.ArrayList ;
32 import java.util.Enumeration ;
33 import java.util.HashMap ;
34 import java.util.HashSet ;
35 import java.util.Iterator ;
36 import java.util.Properties ;
37 import java.util.ResourceBundle ;
38 import java.util.Set ;
39
40 // Log4J Imports
41 import org.apache.log4j.Category;
42
43 // darrt imports
44 import org.enableit.db.darrt.schema.Provider ;
45 import org.enableit.db.darrt.schema.ProviderExt ;
46
47 /**
48 * <p>General class intended to hide the process of obtaining
49 * database connections to any database </p>
50 *
51 * <p>The connection properties such as server name, username/password etc
52 * are all stored in a file named db.properties in the package level
53 * directory matching this class (ie <code>thestephensons.db</code>) </p>
54 *
55 * <p>Internal methods then arrange these in the way to construct a
56 * Connection of the required type. Because this may vary from one vendor
57 * to the next, internally specific methods exist for each database driver
58 * vendor</p>
59 *
60 * <p>Supported database drivers currently are: </p>
61 * <ul>
62 * <li>ASE/ASA (Sybase jConnect 5.x)</li>
63 * <li>MySQL (MM.MySQL driver)</li>
64 * </ul>
65 *
66 * @version v1.0
67 *
68 * @author __AUTHOR__
69 *
70 */
71 public class ConnectionFactory {
72
73 /*
74 * Constructors
75 */
76 /**
77 * Default Constructor
78 */
79 private ConnectionFactory() {
80 cat.info("METHOD_ENTRY constructor") ;
81
82 // Need to initialise with something, use props file in case no JNDI available
83 props = new Properties() ;
84 try {
85 // Check class instance and get source properties
86 InputStream is = this.getClass().getResourceAsStream("db.properties") ;
87 props.load(is) ;
88
89 configureFromProps() ;
90 } catch (NullPointerException e) {
91 cat.warn( "WARNING: Unable to read db connection properties: "
92 + e.getMessage() ) ;
93 } catch (IOException e) {
94 cat.warn( "WARNING: Unable to read db connection properties: "
95 + e.getMessage() ) ;
96 // throw new DBException("ERROR: Getting db connection properties: " + e.getMessage()) ;
97 }
98
99 }
100
101 /*
102 * Methods
103 */
104
105 /**
106 * Factory method.
107 * @return The <code>ConnectionFactory</code> singleton.
108 */
109 public static synchronized ConnectionFactory getInstance() {
110 cat.info("METHOD_ENTRY getInstance") ;
111
112 if (me == null) me = new ConnectionFactory() ;
113
114 cat.info("METHOD_EXIT getInstance") ;
115 return me ;
116 }
117
118 /**
119 * Set a connection property dynamically
120 *
121 */
122 public static void setProperty(String key, String value) {
123 cat.info("METHOD_ENTRY setProperty") ;
124
125 // Ensure we are initialised
126 getInstance() ;
127 props.setProperty(key,value) ;
128 }
129
130 /**
131 * Interpretes the props to create a Provider for each distinct data source
132 * configuration.
133 * throws DBException If the properties are invalid in some way
134 */
135 private void configureFromProps() {
136 cat.info("METHOD_ENTRY configureFromProps") ;
137 try {
138 // Find all config names, use USER to distinguish
139 for (Enumeration enum = props.propertyNames() ;
140 enum.hasMoreElements() ;) {
141 String name = (String) enum.nextElement() ;
142 if (name.indexOf("USER") > -1) {
143 String s = null ;
144 int i = name.indexOf(".USER") ;
145 if (i > -1) {
146 s = name.substring(0, i) ;
147 ProviderExt prov = new ProviderExt() ;
148 prov.setConnectionProperties(
149 props.getProperty(s + ".DRIVER"),
150 props.getProperty(s + ".URL"),
151 props.getProperty(s + ".USER"),
152 props.getProperty(s + ".PASSWORD"),
153 props.getProperty(s + ".SCHEMA")) ;
154 connConfigs.put(s, prov) ;
155 } else {
156 s = "default" ;
157 // Clearly there can be only one of these!
158 ProviderExt prov = new ProviderExt() ;
159 prov.setConnectionProperties(
160 props.getProperty("DRIVER"),
161 props.getProperty("URL"),
162 props.getProperty("USER"),
163 props.getProperty("PASSWORD"),
164 props.getProperty("SCHEMA")) ;
165 connConfigs.put("default", prov) ;
166 }
167 }
168 }
169 } catch (Exception e) {
170 cat.warn(e.getMessage(), e) ;
171 //throw new DBException(e.getMessage());
172 }
173 cat.info("METHOD_EXIT configureFromProps") ;
174 }
175
176 /**
177 * Returns the names of configured database connections.
178 *
179 * <p>These are names that may be used successfully with
180 * <code>getConnectionByName</code>.
181 *
182 * @return <code>java.util.Set</code> of connection names (of type
183 * <code>String</code>).
184 * @see #getProvider
185 */
186 public Set getConnectionNames() {
187 cat.info("METHOD_ENTRY getConnectionNames") ;
188
189 Set names = null ;
190 if (getInstance().connConfigs != null
191 && !getInstance().connConfigs.isEmpty()) {
192 names = getInstance().connConfigs.keySet() ;
193 } else {
194 names = new HashSet() ;
195 }
196
197 cat.info("METHOD_EXIT getConnectionNames, returning =" + names) ;
198 return names ;
199 }
200
201 /**
202 * Obtain connection details for the named configuration.
203 *
204 * @param name configuration name.
205 * @return Provider holding the connection details
206 * @throws DBException If the named configuration is not found.
207 * @see #getConnectionNames
208 */
209 public Provider getProvider(String name)
210 throws DBException {
211 cat.info("METHOD_ENTRY getProvider name=" + name) ;
212
213 Provider provider = null ;
214 if (getInstance().connConfigs != null
215 && !getInstance().connConfigs.isEmpty()
216 && getInstance().connConfigs.containsKey(name)) {
217 provider = (Provider) getInstance().connConfigs.get(name) ;
218 } else {
219 throw new DBException("No connection configuration exists "
220 + "with name: " + name) ;
221 }
222
223 cat.info("METHOD_EXIT getProvider") ;
224 return provider ;
225 }
226
227 /**
228 * Obtain connection for the named configuration.
229 *
230 * @param name configuration name.
231 * @return Provider holding the connection details
232 * @throws DBException If the named configuration is not found.
233 * @see #getConnectionNames
234 */
235 public Connection getConnection(String name)
236 throws DBException {
237 cat.info("METHOD_ENTRY getConnection name=" + name) ;
238
239 Connection conn = null ;
240 if (getInstance().connConfigs != null
241 && !getInstance().connConfigs.isEmpty()
242 && getInstance().connConfigs.containsKey(name)) {
243 Provider provider = (Provider) getInstance().connConfigs.get(name) ;
244 conn = getConnection(provider) ;
245 } else {
246 throw new DBException("No connection configuration exists "
247 + "with name: " + name) ;
248 }
249
250 cat.info("METHOD_EXIT getConnection") ;
251 return conn ;
252 }
253
254 /**
255 * Returns a connection to the database identified by the
256 * class's resource properties
257 *
258 * @return Connection
259 *
260 * @throws DBException
261 * in the event of a problem creating the connection
262 */
263 public static Connection getConnection()
264 throws DBException {
265 cat.info("METHOD_ENTRY getConnection") ;
266
267 Connection conn = null ;
268 try {
269 conn = getInstance().getConnection(props) ;
270 } catch (DBException dbe) {
271 // simply rethrow
272 throw dbe ;
273 } catch (Exception e) {
274 cat.error( "ERROR: " + e.getMessage() ) ;
275
276 throw new DBException("ERROR: " + e.getMessage()) ;
277 }
278
279 if (conn==null) {
280 cat.error( "ERROR: Unable to create connection" ) ;
281 throw new DBException("ERROR: Unable to create connection: ") ;
282 }
283
284 cat.info("METHOD_EXIT") ;
285 return conn ;
286 }
287
288 /**
289 * Returns a connection to the database identified by the
290 * supplied properties.
291 * <p>
292 * Parameters may be empty strings, but may not be null.
293 *
294 * @param driver
295 * @param database
296 * @param server
297 * @param port
298 * @param protocol
299 * @param user
300 * @param password
301 *
302 * @return Connection
303 *
304 * @throws DBException
305 * in the event of a problem creating the connection
306 */
307 public static Connection getConnection( String driver,
308 String database,
309 String server,
310 String port,
311 String protocol,
312 String user,
313 String password )
314 throws DBException {
315 cat.info("METHOD_ENTRY getConnection, driver=" + driver + ", database="
316 + database + ", server=" + server + ", port=" + port
317 + ", protocol=" + protocol + ", user=" + user + ", password="
318 + password) ;
319
320 Connection conn = null ;
321 try {
322 // Check class instance and get propsource properties
323 getInstance() ;
324
325 props.put("DRIVER", driver) ;
326 props.put("DATABASE_NAME", database) ;
327 props.put("SERVER_NAME", server) ;
328 props.put("PORT", port) ;
329 props.put("PROTOCOL", protocol) ;
330 props.put("USER", user) ;
331 props.put("PASSWORD", password) ;
332
333 conn = getInstance().getConnection(props) ;
334 } catch (DBException dbe) {
335 // simply rethrow
336 throw dbe ;
337 } catch (Exception e) {
338 cat.error( "ERROR: " + e.getMessage() ) ;
339
340 throw new DBException("ERROR: " + e.getMessage()) ;
341 }
342
343 if (conn == null) {
344 cat.error( "ERROR: Unable to create connection" ) ;
345 throw new DBException("ERROR: Unable to create connection: ") ;
346 }
347
348 cat.info("METHOD_EXIT getConnection") ;
349 return conn ;
350 }
351
352 /**
353 * Returns a connection to the database identified by the
354 * supplied properties.
355 * <p>
356 * Parameters may be empty strings, but may not be null.
357 *
358 * @param driver
359 * @param url
360 * @param user
361 * @param password
362 *
363 * @return Connection
364 *
365 * @throws DBException
366 * in the event of a problem creating the connection
367 */
368 public static Connection getConnection( String driver,
369 String url,
370 String user,
371 String password )
372 throws DBException {
373 cat.info("METHOD_ENTRY getConnection, driver=" + driver + ", url=" + url
374 + ", user=" + user + ", password=" + password) ;
375
376 Connection conn = null ;
377 try {
378 // Check class instance and set source properties
379 getInstance() ;
380
381 props.put("DRIVER", driver) ;
382 props.put("URL", url) ;
383 props.put("USER", user) ;
384 props.put("PASSWORD", password) ;
385
386 conn = getInstance().getConnection(props) ;
387 } catch (DBException dbe) {
388 // simply rethrow
389 throw dbe ;
390 } catch (Exception e) {
391 cat.error( e.getMessage() ) ;
392
393 throw new DBException("ERROR: " + e.getMessage()) ;
394 }
395
396 if (conn == null) {
397 cat.error( "ERROR: Unable to create connection" ) ;
398 throw new DBException("ERROR: Unable to create connection: ") ;
399 }
400
401 cat.info("METHOD_EXIT getConnection") ;
402 return conn ;
403 }
404
405 /**
406 * Returns a connection to the database identified within the
407 * supplied provider.
408 *
409 * <p>Parameters may be empty strings, but may not be null. </p>
410 *
411 * @param provider Contains connection details.
412 *
413 * @return Connection
414 *
415 * @throws DBException
416 * in the event of a problem creating the connection
417 */
418 public static Connection getConnection(Provider provider)
419 throws DBException {
420 cat.info("METHOD_ENTRY getConnection(Provider)") ;
421
422 Connection conn = getInstance()
423 .getConnection(provider.getDriver().getClassName(),
424 provider.getUrl(), provider.getUsername(),
425 provider.getPassword()) ;
426
427 cat.info("METHOD_EXIT getConnection(Provider)") ;
428 return conn ;
429 }
430
431 /**
432 * Returns a connection to the database identified by the
433 * <code>Properties</code> supplied
434 *
435 * @return Connection
436 *
437 * @throws DBException
438 * in the event of a problem creating the connection
439 */
440 private Connection getConnection( Properties props )
441 throws DBException {
442 cat.info("METHOD_ENTRY getConnection, Properties=") ;
443
444 Connection conn = null ;
445 try {
446
447 if (props.getProperty("URL")!=null) {
448 conn = getUrlConnection(props) ;
449 } else {
450 String driver = props.getProperty("DRIVER") ;
451 if (driver.equals("org.gjt.mm.mysql.Driver")) {
452 conn = getMySQLConnection(props) ;
453 } else if (driver.equals("com.sybase.jdbc2.jdbc.SybDriver") ) {
454 conn = getSybConnection(props) ;
455 } else {
456 cat.error( "ERROR: Unsupported driver: " + driver ) ;
457 throw new DBException("ERROR: Unsupported driver: " + driver) ;
458 }
459 }
460 } catch (SQLException sqle) {
461 cat.error( "ERROR: Getting connection: " + sqle.getMessage() ) ;
462 throw new DBException("ERROR: Getting connection: " + sqle.getMessage()) ;
463 } catch (DBException e) {
464 // simply rethrow
465 throw e ;
466 } catch (Exception e) {
467 cat.error( "ERROR: " + e.getMessage() ) ;
468 //e.printStackTrace() ;
469
470 throw new DBException("ERROR: " + e.getMessage()) ;
471 }
472
473 if (conn==null) {
474 cat.error( "ERROR: Unable to create connection" ) ;
475 throw new DBException("ERROR: Unable to create connection: ") ;
476 }
477
478 cat.info("METHOD_EXIT getConnection") ;
479 return conn ;
480 }
481
482
483 private Connection getMySQLConnection( Properties props )
484 throws java.sql.SQLException, DBException {
485 cat.info("METHOD_ENTRY getConnection, Properties=" + props) ;
486
487 Object driver = getDriver(props.getProperty("DRIVER")) ;
488
489 String connectionString = "jdbc:" ;
490 connectionString += props.getProperty("PROTOCOL") ;
491 connectionString += "://" ;
492 connectionString += props.getProperty("SERVER_NAME") ;
493 connectionString += "/" ;
494 connectionString += props.getProperty("DATABASE_NAME") ;
495 connectionString += "?" ;
496 connectionString += "user=" ;
497 connectionString += props.getProperty("USER") ;
498 connectionString += "&" ;
499 connectionString += "password=" ;
500 connectionString += props.getProperty("PASSWORD") ;
501
502 cat.info("Connection string: " + connectionString);
503 Connection conn = DriverManager.getConnection(connectionString);
504
505 cat.info("METHOD_EXIT getMySQLConnection") ;
506 return conn ;
507
508 }
509
510 private Connection getSybConnection( Properties props )
511 throws java.sql.SQLException, DBException {
512 cat.info("METHOD_ENTRY, props=" + props) ;
513
514 Object driver = getDriver(props.getProperty("DRIVER")) ;
515
516 String connectionString = null ;
517 if ( props.getProperty("URL")==null) {
518 connectionString = "jdbc:sybase:" ;
519 connectionString += props.getProperty("PROTOCOL") ;
520 connectionString += ":" ;
521 connectionString += props.getProperty("SERVER_NAME") ;
522 connectionString += ":" ;
523 connectionString += props.getProperty("PORT") ;
524 } else {
525 connectionString = props.getProperty("URL") ;
526 }
527
528 Properties jcprops = new Properties() ;
529 jcprops.put("user", props.getProperty("USER")) ;
530 jcprops.put("password", props.getProperty("PASSWORD")) ;
531
532 cat.info("Connection string: " + connectionString);
533 Connection conn = DriverManager.getConnection(connectionString, jcprops);
534
535 cat.info("METHOD_EXIT getSybConnection") ;
536 return conn ;
537 }
538
539 private Connection getUrlConnection(Properties props)
540 throws java.sql.SQLException, DBException {
541 cat.info("METHOD_ENTRY getUrlConnection, props=" + props) ;
542
543 Object driver = getDriver(props.getProperty("DRIVER")) ;
544
545 String connectionString = props.getProperty("URL") ;
546
547 Properties jcprops = new Properties() ;
548 jcprops.put("user", props.getProperty("USER")) ;
549 jcprops.put("password", props.getProperty("PASSWORD")) ;
550
551 cat.info("Connection string: " + connectionString);
552 Connection conn = DriverManager.getConnection(connectionString, jcprops);
553
554 cat.info("METHOD_EXIT getUrlConnection") ;
555 return conn ;
556 }
557
558 /**
559 * Obtain a JDBC driver class.
560 */
561 protected Object getDriver(String className)
562 throws DBException {
563 cat.info("METHOD_ENTRY: getDriver, className=" + className) ;
564
565 Object driver = null ;
566 try {
567 ClassLoader cl = ConnectionFactory.class.getClassLoader() ;
568 driver = cl.loadClass(className).newInstance() ;
569 } catch (IllegalAccessException iae) {
570 cat.error(iae.getMessage()) ;
571 throw new DBException("Problem getting db connection properties: " + iae.getMessage()) ;
572 } catch (ClassNotFoundException cnfe) {
573 cat.error(cnfe.getMessage()) ;
574
575 System.out.println("Searched classpath:" +
576 System.getProperty("java.class.path")) ;
577 cat.debug("Searched classpath:" +
578 System.getProperty("java.class.path")) ;
579
580 try {
581 cat.warn("Attempting to find driver thru system classloader") ;
582
583 ClassLoader scl = ClassLoader.getSystemClassLoader() ;
584 driver = scl.loadClass(className).newInstance() ;
585 } catch (Exception e2) {
586 throw new DBException("Driver class not found: " + e2.getMessage()) ;
587 }
588
589 } catch (InstantiationException ie) {
590 cat.error(ie.getMessage()) ;
591 throw new DBException("Problem getting db connection properties: " + ie.getMessage()) ;
592 }
593
594 cat.info("METHOD_EXIT: getDriver") ;
595 return driver ;
596 }
597
598 /*
599 * Properties
600 */
601
602 /**
603 * Holds the <code>Provider</code> instances that have been configured as
604 * data sources.
605 * @see org.enableit.db.darrt.schema.Provider
606 */
607 private static HashMap connConfigs = new HashMap() ;
608
609 /**
610 * The Log4J <code>Category</code> doing the logging.
611 * Same <code>Category</code> is used throughout the package.
612 */
613 protected static Category cat = Category.getInstance(ConnectionFactory.class);
614
615 /**
616 * Reference to the singleton instance
617 *
618 * NB the singleton status is not enforced via a synchronise statement
619 */
620 private static ConnectionFactory me ;
621
622 /**
623 * The current database properties to make connections with
624 */
625 private static Properties props = new Properties() ;
626
627 /**
628 * Information on the exact CVS version accessible after compilation
629 */
630 public static final String about = "$Revision: 1.5 $";
631
632 }