1 package org.apache.torque;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.sql.Connection;
20 import java.sql.SQLException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.configuration.Configuration;
29 import org.apache.commons.configuration.ConfigurationException;
30 import org.apache.commons.configuration.PropertiesConfiguration;
31
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 import org.apache.torque.adapter.DB;
37 import org.apache.torque.adapter.DBFactory;
38 import org.apache.torque.dsfactory.DataSourceFactory;
39 import org.apache.torque.manager.AbstractBaseManager;
40 import org.apache.torque.map.DatabaseMap;
41 import org.apache.torque.map.TableMap;
42 import org.apache.torque.oid.IDBroker;
43 import org.apache.torque.oid.IDGeneratorFactory;
44 import org.apache.torque.util.BasePeer;
45
46 /***
47 * The core of Torque's implementation. Both the classic {@link
48 * org.apache.torque.Torque} static wrapper and the {@link
49 * org.apache.torque.avalon.TorqueComponent} <a
50 * href="http://avalon.apache.org/">Avalon</a> implementation leverage
51 * this class.
52 *
53 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
54 * @author <a href="mailto:magnus@handtolvur.is">Magn�s ��r Torfason</a>
55 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
56 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
57 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
58 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
59 * @author <a href="mailto:kschrader@karmalab.org">Kurt Schrader</a>
60 * @version $Id: TorqueInstance.java,v 1.5.2.5 2004/08/23 02:54:16 seade Exp $
61 */
62 public class TorqueInstance
63 {
64 /*** Logging */
65 private static Log log = LogFactory.getLog(TorqueInstance.class);
66
67 /*** A constant for <code>default</code>. */
68 private static final String DEFAULT_NAME = "default";
69
70 /*** The db name that is specified as the default in the property file */
71 private String defaultDBName;
72
73 /*** The global cache of database maps */
74 private Map dbMaps;
75
76 /*** The cache of DataSourceFactory's */
77 private Map dsFactoryMap;
78
79 /*** The cache of DB adapter keys */
80 private Map adapterMap;
81
82 /*** A repository of Manager instances. */
83 private Map managers;
84
85 /*** Torque-specific configuration. */
86 private Configuration conf;
87
88 /*** flag to set to true once this class has been initialized */
89 private boolean isInit = false;
90
91 /***
92 * Store mapbuilder classnames for peers that have been referenced prior
93 * to Torque being initialized. This can happen if torque om/peer objects
94 * are serialized then unserialized prior to Torque being reinitialized.
95 * This condition exists in a normal catalina restart.
96 */
97 private List mapBuilders = null;
98
99 /***
100 * Creates a new instance with default configuration.
101 *
102 * @see #resetConfiguration()
103 */
104 public TorqueInstance()
105 {
106 resetConfiguration();
107 }
108
109 /***
110 * Initializes this instance of Torque.
111 *
112 * @see org.apache.stratum.lifecycle.Initializable
113 * @throws TorqueException Any exceptions caught during processing will be
114 * rethrown wrapped into a TorqueException.
115 */
116 private synchronized void initialize() throws TorqueException
117 {
118 log.debug("initialize()");
119
120 if (isInit)
121 {
122 log.debug("Multiple initializations of Torque attempted");
123 return;
124 }
125
126 if (conf == null)
127 {
128 throw new TorqueException("Torque cannot be initialized without "
129 + "a valid configuration. Please check the log files "
130 + "for further details.");
131 }
132
133
134
135
136
137
138
139 Configuration subConf = conf.subset("torque");
140
141 if (!subConf.isEmpty())
142 {
143 setConfiguration(subConf);
144 }
145
146 dbMaps = new HashMap();
147 initAdapters(conf);
148 initDataSourceFactories(conf);
149
150 for (Iterator i = mapBuilders.iterator(); i.hasNext();)
151 {
152
153 BasePeer.getMapBuilder((String) i.next());
154 }
155
156 mapBuilders = null;
157
158
159 initManagerMappings(conf);
160
161 isInit = true;
162 }
163
164 /***
165 *
166 * @param conf the Configuration representing the properties file
167 * @throws TorqueException Any exceptions caught during processing will be
168 * rethrown wrapped into a TorqueException.
169 */
170 private final void initAdapters(Configuration conf)
171 throws TorqueException
172 {
173 log.debug("initAdapters(" + conf + ")");
174 adapterMap = new HashMap();
175 Configuration c = conf.subset("database");
176
177 if (c != null)
178 {
179 boolean foundAdapters = false;
180
181 try
182 {
183 for (Iterator it = c.getKeys(); it.hasNext(); )
184 {
185 String key = (String) it.next();
186 if (key.endsWith("adapter"))
187 {
188 String adapter = c.getString(key);
189 String handle = key.substring(0, key.indexOf('.'));
190 DB db = DBFactory.create(adapter);
191
192 adapterMap.put(handle, db);
193 log.debug("Adding " + adapter + " -> " + handle + " as Adapter");
194 foundAdapters = true;
195 }
196 }
197 if (!foundAdapters)
198 {
199 log.warn("Databases defined but no adapter "
200 + "configurations found!");
201 }
202 }
203 catch (Exception e)
204 {
205 log.error("Error reading configuration seeking database "
206 + "adapters", e);
207 throw new TorqueException(e);
208 }
209 }
210 else
211 {
212 log.warn("No Database definitions found!");
213 }
214
215 }
216
217 /***
218 *
219 * @param conf the Configuration representing the properties file
220 * @throws TorqueException Any exceptions caught during processing will be
221 * rethrown wrapped into a TorqueException.
222 */
223 private void initDataSourceFactories(Configuration conf)
224 throws TorqueException
225 {
226 log.debug("initDataSourceFactories(" + conf + ")");
227 dsFactoryMap = new HashMap();
228 Configuration c = conf.subset("dsfactory");
229 if (c != null)
230 {
231 boolean foundFactories = false;
232
233 try
234 {
235 for (Iterator it = c.getKeys(); it.hasNext();)
236 {
237 String key = (String) it.next();
238 if (key.endsWith("factory"))
239 {
240 String classname = c.getString(key);
241 String handle = key.substring(0, key.indexOf('.'));
242 log.debug("handle: " + handle
243 + " DataSourceFactory: " + classname);
244 Class dsfClass = Class.forName(classname);
245 DataSourceFactory dsf =
246 (DataSourceFactory) dsfClass.newInstance();
247 dsf.initialize(c.subset(handle));
248 dsFactoryMap.put(handle, dsf);
249 foundFactories = true;
250 }
251 }
252 if (!foundFactories)
253 {
254 log.warn("Data Sources configured but no factories found!");
255 }
256 }
257 catch (Exception e)
258 {
259 log.error("Error reading adapter configuration", e);
260 throw new TorqueException(e);
261 }
262 }
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277 String defaultDB = getDefaultDB();
278
279 if (dsFactoryMap.get(DEFAULT_NAME) == null
280 && !defaultDB.equals(DEFAULT_NAME))
281 {
282 log.debug("Adding a dummy entry for "
283 + DEFAULT_NAME + ", mapped onto " + defaultDB);
284 dsFactoryMap.put(DEFAULT_NAME, dsFactoryMap.get(defaultDB));
285 }
286 }
287
288 /***
289 * Initialization of Torque with a properties file.
290 *
291 * @param configFile The absolute path to the configuration file.
292 * @throws TorqueException Any exceptions caught during processing will be
293 * rethrown wrapped into a TorqueException.
294 */
295 public void init(String configFile)
296 throws TorqueException
297 {
298 log.debug("init(" + configFile + ")");
299 try
300 {
301 Configuration conf = new PropertiesConfiguration(configFile);
302
303 log.debug("Config Object is " + conf);
304 init(conf);
305 }
306 catch (ConfigurationException e)
307 {
308 throw new TorqueException(e);
309 }
310 }
311
312 /***
313 * Initialization of Torque with a properties file.
314 *
315 * @param conf The Torque configuration.
316 * @throws TorqueException Any exceptions caught during processing will be
317 * rethrown wrapped into a TorqueException.
318 */
319 public void init(Configuration conf)
320 throws TorqueException
321 {
322 log.debug("init(" + conf + ")");
323 setConfiguration(conf);
324 initialize();
325 }
326
327
328 /***
329 * Creates a mapping between classes and their manager classes.
330 *
331 * The mapping is built according to settings present in
332 * properties file. The entries should have the
333 * following form:
334 *
335 * <pre>
336 * torque.managed_class.com.mycompany.Myclass.manager= \
337 * com.mycompany.MyManagerImpl
338 * services.managed_class.com.mycompany.Myotherclass.manager= \
339 * com.mycompany.MyOtherManagerImpl
340 * </pre>
341 *
342 * <br>
343 *
344 * Generic ServiceBroker provides no Services.
345 *
346 * @param conf the Configuration representing the properties file
347 * @throws TorqueException Any exceptions caught during processing will be
348 * rethrown wrapped into a TorqueException.
349 */
350 protected void initManagerMappings(Configuration conf)
351 throws TorqueException
352 {
353 int pref = Torque.MANAGER_PREFIX.length();
354 int suff = Torque.MANAGER_SUFFIX.length();
355
356 for (Iterator it = conf.getKeys(); it.hasNext();)
357 {
358 String key = (String) it.next();
359
360 if (key.startsWith(Torque.MANAGER_PREFIX)
361 && key.endsWith(Torque.MANAGER_SUFFIX))
362 {
363 String managedClassKey = key.substring(pref,
364 key.length() - suff);
365 if (!managers.containsKey(managedClassKey))
366 {
367 String managerClass = conf.getString(key);
368 log.info("Added Manager for Class: " + managedClassKey
369 + " -> " + managerClass);
370 try
371 {
372 initManager(managedClassKey, managerClass);
373 }
374 catch (TorqueException e)
375 {
376
377
378
379 log.error("", e);
380 e.printStackTrace();
381 throw e;
382 }
383 }
384 }
385 }
386 }
387
388 /***
389 * Initialize a manager
390 *
391 * @param name name of the manager
392 * @param className name of the manager class
393 * @throws TorqueException Any exceptions caught during processing will be
394 * rethrown wrapped into a TorqueException.
395 */
396 private synchronized void initManager(String name, String className)
397 throws TorqueException
398 {
399 AbstractBaseManager manager = (AbstractBaseManager) managers.get(name);
400
401 if (manager == null)
402 {
403 if (className != null && className.length() != 0)
404 {
405 try
406 {
407 manager = (AbstractBaseManager)
408 Class.forName(className).newInstance();
409 managers.put(name, manager);
410 }
411 catch (Exception e)
412 {
413 throw new TorqueException("Could not instantiate "
414 + "manager associated with class: "
415 + name, e);
416 }
417 }
418 }
419 }
420
421 /***
422 * Determine whether Torque has already been initialized.
423 *
424 * @return true if Torque is already initialized
425 */
426 public boolean isInit()
427 {
428 return isInit;
429 }
430
431 /***
432 * Sets the configuration for Torque and all dependencies.
433 *
434 * @param conf the Configuration
435 */
436 public void setConfiguration(Configuration conf)
437 {
438 log.debug("setConfiguration(" + conf + ")");
439 this.conf = conf;
440 }
441
442 /***
443 * Get the configuration for this component.
444 *
445 * @return the Configuration
446 */
447 public Configuration getConfiguration()
448 {
449 log.debug("getConfiguration() = " + conf);
450 return conf;
451 }
452
453 /***
454 * This method returns a Manager for the given name.
455 *
456 * @param name name of the manager
457 * @return a Manager
458 */
459 public AbstractBaseManager getManager(String name)
460 {
461 AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
462 if (m == null)
463 {
464 log.error("No configured manager for key " + name + ".");
465 }
466 return m;
467 }
468
469 /***
470 * This methods returns either the Manager from the configuration file,
471 * or the default one provided by the generated code.
472 *
473 * @param name name of the manager
474 * @param defaultClassName the class to use if name has not been configured
475 * @return a Manager
476 */
477 public AbstractBaseManager getManager(String name,
478 String defaultClassName)
479 {
480 AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
481 if (m == null)
482 {
483 log.debug("Added late Manager mapping for Class: "
484 + name + " -> " + defaultClassName);
485
486 try
487 {
488 initManager(name, defaultClassName);
489 }
490 catch (TorqueException e)
491 {
492 log.error(e.getMessage(), e);
493 }
494
495
496 m = (AbstractBaseManager) managers.get(name);
497 }
498
499 return m;
500 }
501
502 /***
503 * Shuts down the service.
504 *
505 * This method halts the IDBroker's daemon thread in all of
506 * the DatabaseMap's.
507 */
508 public synchronized void shutdown()
509 {
510 if (dbMaps != null)
511 {
512 for (Iterator it = dbMaps.values().iterator(); it.hasNext();)
513 {
514 DatabaseMap map = (DatabaseMap) it.next();
515 IDBroker idBroker = map.getIDBroker();
516 if (idBroker != null)
517 {
518 idBroker.stop();
519 }
520 }
521 }
522 resetConfiguration();
523 }
524
525 /***
526 * Resets some internal configuration variables to
527 * their defaults.
528 */
529 private void resetConfiguration()
530 {
531 mapBuilders = Collections.synchronizedList(new ArrayList());
532 managers = new HashMap();
533 isInit = false;
534 }
535
536 /***
537 * Returns the default database map information.
538 *
539 * @return A DatabaseMap.
540 * @throws TorqueException Any exceptions caught during processing will be
541 * rethrown wrapped into a TorqueException.
542 */
543 public DatabaseMap getDatabaseMap()
544 throws TorqueException
545 {
546 return getDatabaseMap(getDefaultDB());
547 }
548
549 /***
550 * Returns the database map information. Name relates to the name
551 * of the connection pool to associate with the map.
552 *
553 * @param name The name of the database corresponding to the
554 * <code>DatabaseMap</code> to retrieve.
555 * @return The named <code>DatabaseMap</code>.
556 * @throws TorqueException Any exceptions caught during processing will be
557 * rethrown wrapped into a TorqueException.
558 */
559 public DatabaseMap getDatabaseMap(String name)
560 throws TorqueException
561 {
562 if (name == null)
563 {
564 throw new TorqueException ("DatabaseMap name was null!");
565 }
566
567 if (dbMaps == null)
568 {
569 throw new TorqueException("Torque was not initialized properly.");
570 }
571
572 synchronized (dbMaps)
573 {
574 DatabaseMap map = (DatabaseMap) dbMaps.get(name);
575 if (map == null)
576 {
577
578 map = initDatabaseMap(name);
579 }
580 return map;
581 }
582 }
583
584 /***
585 * Creates and initializes the mape for the named database.
586 * Assumes that <code>dbMaps</code> member is sync'd.
587 *
588 * @param name The name of the database to map.
589 * @return The desired map.
590 * @throws TorqueException Any exceptions caught during processing will be
591 * rethrown wrapped into a TorqueException.
592 */
593 private final DatabaseMap initDatabaseMap(String name)
594 throws TorqueException
595 {
596 DatabaseMap map = new DatabaseMap(name);
597
598
599 setupIdTable(map);
600
601
602 try
603 {
604 String key = getDatabaseProperty(name, "adapter");
605 if (StringUtils.isEmpty(key))
606 {
607 key = getDatabaseProperty(name, "driver");
608 }
609 DB db = DBFactory.create(key);
610 for (int i = 0; i < IDGeneratorFactory.ID_GENERATOR_METHODS.length;
611 i++)
612 {
613 map.addIdGenerator(IDGeneratorFactory.ID_GENERATOR_METHODS[i],
614 IDGeneratorFactory.create(db));
615 }
616 }
617 catch (java.lang.InstantiationException e)
618 {
619 throw new TorqueException(e);
620 }
621
622
623
624 Map newMaps = new HashMap(dbMaps);
625 newMaps.put(name, map);
626 dbMaps = newMaps;
627
628 return map;
629 }
630
631 /***
632 * Register a MapBuilder
633 *
634 * @param className the MapBuilder
635 */
636 public void registerMapBuilder(String className)
637 {
638 mapBuilders.add(className);
639 }
640
641 /***
642 * Returns the specified property of the given database, or the empty
643 * string if no value is set for the property.
644 *
645 * @param db The name of the database whose property to get.
646 * @param prop The name of the property to get.
647 * @return The property's value.
648 */
649 private String getDatabaseProperty(String db, String prop)
650 {
651 return conf.getString(new StringBuffer("database.")
652 .append(db)
653 .append('.')
654 .append(prop)
655 .toString(), "");
656 }
657
658 /***
659 * Setup IDBroker's table information within given database map.
660 *
661 * This method should be called on all new database map to ensure that
662 * IDBroker functionality is available in all databases used by the
663 * application.
664 *
665 * @param map the DataBaseMap to setup.
666 */
667 private final void setupIdTable(DatabaseMap map)
668 {
669 map.setIdTable("ID_TABLE");
670 TableMap tMap = map.getIdTable();
671 tMap.addPrimaryKey("ID_TABLE_ID", new Integer(0));
672 tMap.addColumn("TABLE_NAME", "");
673 tMap.addColumn("NEXT_ID", new Integer(0));
674 tMap.addColumn("QUANTITY", new Integer(0));
675 }
676
677 /***
678 * This method returns a Connection from the default pool.
679 *
680 * @return The requested connection.
681 * @throws TorqueException Any exceptions caught during processing will be
682 * rethrown wrapped into a TorqueException.
683 */
684 public Connection getConnection()
685 throws TorqueException
686 {
687 return getConnection(getDefaultDB());
688 }
689
690 /***
691 *
692 * @param name The database name.
693 * @return a database connection
694 * @throws TorqueException Any exceptions caught during processing will be
695 * rethrown wrapped into a TorqueException.
696 */
697 public Connection getConnection(String name)
698 throws TorqueException
699 {
700 Connection con = null;
701 DataSourceFactory dsf = null;
702 try
703 {
704 dsf = (DataSourceFactory) dsFactoryMap.get(name);
705 con = dsf.getDataSource().getConnection();
706 }
707 catch (Exception e)
708 {
709 if (dsf == null && e instanceof NullPointerException)
710 {
711 throw new NullPointerException(
712 "There was no DataSourceFactory "
713 + "configured for the connection " + name);
714 }
715 else
716 {
717 throw new TorqueException(e);
718 }
719 }
720 return con;
721 }
722
723 /***
724 * This method returns a Connecton using the given parameters.
725 * You should only use this method if you need user based access to the
726 * database!
727 *
728 * @param name The database name.
729 * @param username The name of the database user.
730 * @param password The password of the database user.
731 * @return A Connection.
732 * @throws TorqueException Any exceptions caught during processing will be
733 * rethrown wrapped into a TorqueException.
734 */
735 public Connection getConnection(String name, String username,
736 String password)
737 throws TorqueException
738 {
739 Connection con = null;
740 DataSourceFactory dsf = null;
741 try
742 {
743 dsf = (DataSourceFactory) dsFactoryMap.get(name);
744 con = dsf.getDataSource().getConnection(username, password);
745 }
746 catch (Exception e)
747 {
748 if (dsf == null && e instanceof NullPointerException)
749 {
750 throw new NullPointerException(
751 "There was no DataSourceFactory "
752 + "configured for the connection " + name);
753 }
754 else
755 {
756 throw new TorqueException(e);
757 }
758 }
759 return con;
760 }
761
762 /***
763 * Returns database adapter for a specific connection pool.
764 *
765 * @param name A pool name.
766 * @return The corresponding database adapter.
767 * @throws TorqueException Any exceptions caught during processing will be
768 * rethrown wrapped into a TorqueException.
769 */
770 public DB getDB(String name) throws TorqueException
771 {
772 return (DB) adapterMap.get(name);
773 }
774
775
776
777 /***
778 * Returns the name of the default database.
779 *
780 * @return name of the default DB
781 */
782 public String getDefaultDB()
783 {
784 if (conf == null)
785 {
786 return DEFAULT_NAME;
787 }
788 else if (defaultDBName == null)
789 {
790
791 defaultDBName =
792 conf.getString(Torque.DATABASE_DEFAULT,
793 DEFAULT_NAME).trim();
794 }
795
796 return defaultDBName;
797 }
798
799 /***
800 * Closes a connection.
801 *
802 * @param con A Connection to close.
803 */
804 public void closeConnection(Connection con)
805 {
806 if (con != null)
807 {
808 try
809 {
810 con.close();
811 }
812 catch (SQLException e)
813 {
814 log.error("Error occured while closing connection.", e);
815 }
816 }
817 }
818 }