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