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.8 2004/08/23 02:54:31 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 = (Configuration)
302 new PropertiesConfiguration(configFile);
303
304 log.debug("Config Object is " + conf);
305 init(conf);
306 }
307 catch (ConfigurationException e)
308 {
309 throw new TorqueException(e);
310 }
311 }
312
313 /***
314 * Initialization of Torque with a properties file.
315 *
316 * @param conf The Torque configuration.
317 * @throws TorqueException Any exceptions caught during processing will be
318 * rethrown wrapped into a TorqueException.
319 */
320 public void init(Configuration conf)
321 throws TorqueException
322 {
323 log.debug("init(" + conf + ")");
324 setConfiguration(conf);
325 initialize();
326 }
327
328
329 /***
330 * Creates a mapping between classes and their manager classes.
331 *
332 * The mapping is built according to settings present in
333 * properties file. The entries should have the
334 * following form:
335 *
336 * <pre>
337 * torque.managed_class.com.mycompany.Myclass.manager= \
338 * com.mycompany.MyManagerImpl
339 * services.managed_class.com.mycompany.Myotherclass.manager= \
340 * com.mycompany.MyOtherManagerImpl
341 * </pre>
342 *
343 * <br>
344 *
345 * Generic ServiceBroker provides no Services.
346 *
347 * @param conf the Configuration representing the properties file
348 * @throws TorqueException Any exceptions caught during processing will be
349 * rethrown wrapped into a TorqueException.
350 */
351 protected void initManagerMappings(Configuration conf)
352 throws TorqueException
353 {
354 int pref = Torque.MANAGER_PREFIX.length();
355 int suff = Torque.MANAGER_SUFFIX.length();
356
357 for (Iterator it = conf.getKeys(); it.hasNext();)
358 {
359 String key = (String) it.next();
360
361 if (key.startsWith(Torque.MANAGER_PREFIX)
362 && key.endsWith(Torque.MANAGER_SUFFIX))
363 {
364 String managedClassKey = key.substring(pref,
365 key.length() - suff);
366 if (!managers.containsKey(managedClassKey))
367 {
368 String managerClass = conf.getString(key);
369 log.info("Added Manager for Class: " + managedClassKey
370 + " -> " + managerClass);
371 try
372 {
373 initManager(managedClassKey, managerClass);
374 }
375 catch (TorqueException e)
376 {
377
378
379
380 log.error("", e);
381 e.printStackTrace();
382 throw e;
383 }
384 }
385 }
386 }
387 }
388
389 /***
390 * Initialize a manager
391 *
392 * @param name name of the manager
393 * @param className name of the manager class
394 * @throws TorqueException Any exceptions caught during processing will be
395 * rethrown wrapped into a TorqueException.
396 */
397 private synchronized void initManager(String name, String className)
398 throws TorqueException
399 {
400 AbstractBaseManager manager = (AbstractBaseManager) managers.get(name);
401
402 if (manager == null)
403 {
404 if (className != null && className.length() != 0)
405 {
406 try
407 {
408 manager = (AbstractBaseManager)
409 Class.forName(className).newInstance();
410 managers.put(name, manager);
411 }
412 catch (Exception e)
413 {
414 throw new TorqueException("Could not instantiate "
415 + "manager associated with class: "
416 + name, e);
417 }
418 }
419 }
420 }
421
422 /***
423 * Determine whether Torque has already been initialized.
424 *
425 * @return true if Torque is already initialized
426 */
427 public boolean isInit()
428 {
429 return isInit;
430 }
431
432 /***
433 * Sets the configuration for Torque and all dependencies.
434 *
435 * @param conf the Configuration
436 */
437 public void setConfiguration(Configuration conf)
438 {
439 log.debug("setConfiguration(" + conf + ")");
440 this.conf = conf;
441 }
442
443 /***
444 * Get the configuration for this component.
445 *
446 * @return the Configuration
447 */
448 public Configuration getConfiguration()
449 {
450 log.debug("getConfiguration() = " + conf);
451 return conf;
452 }
453
454 /***
455 * This method returns a Manager for the given name.
456 *
457 * @param name name of the manager
458 * @return a Manager
459 */
460 public AbstractBaseManager getManager(String name)
461 {
462 AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
463 if (m == null)
464 {
465 log.error("No configured manager for key " + name + ".");
466 }
467 return m;
468 }
469
470 /***
471 * This methods returns either the Manager from the configuration file,
472 * or the default one provided by the generated code.
473 *
474 * @param name name of the manager
475 * @param defaultClassName the class to use if name has not been configured
476 * @return a Manager
477 */
478 public AbstractBaseManager getManager(String name,
479 String defaultClassName)
480 {
481 AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
482 if (m == null)
483 {
484 log.debug("Added late Manager mapping for Class: "
485 + name + " -> " + defaultClassName);
486
487 try
488 {
489 initManager(name, defaultClassName);
490 }
491 catch (TorqueException e)
492 {
493 log.error(e.getMessage(), e);
494 }
495
496
497 m = (AbstractBaseManager) managers.get(name);
498 }
499
500 return m;
501 }
502
503 /***
504 * Shuts down the service.
505 *
506 * This method halts the IDBroker's daemon thread in all of
507 * the DatabaseMap's.
508 */
509 public synchronized void shutdown()
510 {
511 if (dbMaps != null)
512 {
513 for (Iterator it = dbMaps.values().iterator(); it.hasNext();)
514 {
515 DatabaseMap map = (DatabaseMap) it.next();
516 IDBroker idBroker = map.getIDBroker();
517 if (idBroker != null)
518 {
519 idBroker.stop();
520 }
521 }
522 }
523 resetConfiguration();
524 }
525
526 /***
527 * Resets some internal configuration variables to
528 * their defaults.
529 */
530 private void resetConfiguration()
531 {
532 mapBuilders = Collections.synchronizedList(new ArrayList());
533 managers = new HashMap();
534 isInit = false;
535 }
536
537 /***
538 * Returns the default database map information.
539 *
540 * @return A DatabaseMap.
541 * @throws TorqueException Any exceptions caught during processing will be
542 * rethrown wrapped into a TorqueException.
543 */
544 public DatabaseMap getDatabaseMap()
545 throws TorqueException
546 {
547 return getDatabaseMap(getDefaultDB());
548 }
549
550 /***
551 * Returns the database map information. Name relates to the name
552 * of the connection pool to associate with the map.
553 *
554 * @param name The name of the database corresponding to the
555 * <code>DatabaseMap</code> to retrieve.
556 * @return The named <code>DatabaseMap</code>.
557 * @throws TorqueException Any exceptions caught during processing will be
558 * rethrown wrapped into a TorqueException.
559 */
560 public DatabaseMap getDatabaseMap(String name)
561 throws TorqueException
562 {
563 if (name == null)
564 {
565 throw new TorqueException ("DatabaseMap name was null!");
566 }
567
568 if (dbMaps == null)
569 {
570 throw new TorqueException("Torque was not initialized properly.");
571 }
572
573 synchronized (dbMaps)
574 {
575 DatabaseMap map = (DatabaseMap) dbMaps.get(name);
576 if (map == null)
577 {
578
579 map = initDatabaseMap(name);
580 }
581 return map;
582 }
583 }
584
585 /***
586 * Creates and initializes the mape for the named database.
587 * Assumes that <code>dbMaps</code> member is sync'd.
588 *
589 * @param name The name of the database to map.
590 * @return The desired map.
591 * @throws TorqueException Any exceptions caught during processing will be
592 * rethrown wrapped into a TorqueException.
593 */
594 private final DatabaseMap initDatabaseMap(String name)
595 throws TorqueException
596 {
597 DatabaseMap map = new DatabaseMap(name);
598
599
600 setupIdTable(map);
601
602
603 try
604 {
605 String key = getDatabaseProperty(name, "adapter");
606 if (StringUtils.isEmpty(key))
607 {
608 key = getDatabaseProperty(name, "driver");
609 }
610 DB db = DBFactory.create(key);
611 for (int i = 0; i < IDGeneratorFactory.ID_GENERATOR_METHODS.length;
612 i++)
613 {
614 map.addIdGenerator(IDGeneratorFactory.ID_GENERATOR_METHODS[i],
615 IDGeneratorFactory.create(db));
616 }
617 }
618 catch (java.lang.InstantiationException e)
619 {
620 throw new TorqueException(e);
621 }
622
623
624
625 Map newMaps = new HashMap(dbMaps);
626 newMaps.put(name, map);
627 dbMaps = newMaps;
628
629 return map;
630 }
631
632 /***
633 * Register a MapBuilder
634 *
635 * @param className the MapBuilder
636 */
637 public void registerMapBuilder(String className)
638 {
639 mapBuilders.add(className);
640 }
641
642 /***
643 * Returns the specified property of the given database, or the empty
644 * string if no value is set for the property.
645 *
646 * @param db The name of the database whose property to get.
647 * @param prop The name of the property to get.
648 * @return The property's value.
649 */
650 private String getDatabaseProperty(String db, String prop)
651 {
652 return conf.getString(new StringBuffer("database.")
653 .append(db)
654 .append('.')
655 .append(prop)
656 .toString(), "");
657 }
658
659 /***
660 * Setup IDBroker's table information within given database map.
661 *
662 * This method should be called on all new database map to ensure that
663 * IDBroker functionality is available in all databases used by the
664 * application.
665 *
666 * @param map the DataBaseMap to setup.
667 */
668 private final void setupIdTable(DatabaseMap map)
669 {
670 map.setIdTable("ID_TABLE");
671 TableMap tMap = map.getIdTable();
672 tMap.addPrimaryKey("ID_TABLE_ID", new Integer(0));
673 tMap.addColumn("TABLE_NAME", "");
674 tMap.addColumn("NEXT_ID", new Integer(0));
675 tMap.addColumn("QUANTITY", new Integer(0));
676 }
677
678 /***
679 * This method returns a Connection from the default pool.
680 *
681 * @return The requested connection.
682 * @throws TorqueException Any exceptions caught during processing will be
683 * rethrown wrapped into a TorqueException.
684 */
685 public Connection getConnection()
686 throws TorqueException
687 {
688 return getConnection(getDefaultDB());
689 }
690
691 /***
692 *
693 * @param name The database name.
694 * @return a database connection
695 * @throws TorqueException Any exceptions caught during processing will be
696 * rethrown wrapped into a TorqueException.
697 */
698 public Connection getConnection(String name)
699 throws TorqueException
700 {
701 Connection con = null;
702 DataSourceFactory dsf = null;
703 try
704 {
705 dsf = (DataSourceFactory) dsFactoryMap.get(name);
706 con = dsf.getDataSource().getConnection();
707 }
708 catch (Exception e)
709 {
710 if (dsf == null && e instanceof NullPointerException)
711 {
712 throw new NullPointerException(
713 "There was no DataSourceFactory "
714 + "configured for the connection " + name);
715 }
716 else
717 {
718 throw new TorqueException(e);
719 }
720 }
721 return con;
722 }
723
724 /***
725 * This method returns a Connecton using the given parameters.
726 * You should only use this method if you need user based access to the
727 * database!
728 *
729 * @param name The database name.
730 * @param username The name of the database user.
731 * @param password The password of the database user.
732 * @return A Connection.
733 * @throws TorqueException Any exceptions caught during processing will be
734 * rethrown wrapped into a TorqueException.
735 */
736 public Connection getConnection(String name, String username,
737 String password)
738 throws TorqueException
739 {
740 Connection con = null;
741 DataSourceFactory dsf = null;
742 try
743 {
744 dsf = (DataSourceFactory) dsFactoryMap.get(name);
745 con = dsf.getDataSource().getConnection(username, password);
746 }
747 catch (Exception e)
748 {
749 if (dsf == null && e instanceof NullPointerException)
750 {
751 throw new NullPointerException(
752 "There was no DataSourceFactory "
753 + "configured for the connection " + name);
754 }
755 else
756 {
757 throw new TorqueException(e);
758 }
759 }
760 return con;
761 }
762
763 /***
764 * Returns database adapter for a specific connection pool.
765 *
766 * @param name A pool name.
767 * @return The corresponding database adapter.
768 * @throws TorqueException Any exceptions caught during processing will be
769 * rethrown wrapped into a TorqueException.
770 */
771 public DB getDB(String name) throws TorqueException
772 {
773 return (DB) adapterMap.get(name);
774 }
775
776
777
778 /***
779 * Returns the name of the default database.
780 *
781 * @return name of the default DB
782 */
783 public String getDefaultDB()
784 {
785 if (conf == null)
786 {
787 return DEFAULT_NAME;
788 }
789 else if (defaultDBName == null)
790 {
791
792 defaultDBName =
793 conf.getString(Torque.DATABASE_DEFAULT,
794 DEFAULT_NAME).trim();
795 }
796
797 return defaultDBName;
798 }
799
800 /***
801 * Closes a connection.
802 *
803 * @param con A Connection to close.
804 */
805 public void closeConnection(Connection con)
806 {
807 if (con != null)
808 {
809 try
810 {
811 con.close();
812 }
813 catch (SQLException e)
814 {
815 log.error("Error occured while closing connection.", e);
816 }
817 }
818 }
819 }