1 package org.apache.torque;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.sql.Connection;
23 import java.sql.SQLException;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Set;
30
31 import org.apache.commons.configuration.Configuration;
32 import org.apache.commons.configuration.ConfigurationException;
33 import org.apache.commons.configuration.PropertiesConfiguration;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.torque.adapter.Adapter;
38 import org.apache.torque.adapter.AdapterFactory;
39 import org.apache.torque.adapter.IDMethod;
40 import org.apache.torque.dsfactory.DataSourceFactory;
41 import org.apache.torque.manager.AbstractBaseManager;
42 import org.apache.torque.map.DatabaseMap;
43 import org.apache.torque.oid.IDBroker;
44 import org.apache.torque.oid.IDGeneratorFactory;
45 import org.apache.torque.om.Persistent;
46 import org.apache.torque.util.Transaction;
47 import org.apache.torque.util.TransactionManager;
48 import org.apache.torque.util.TransactionManagerImpl;
49
50 /**
51 * The core of Torque's implementation. Both the classic {@link
52 * org.apache.torque.Torque} static wrapper and the {@link
53 * org.apache.torque.avalon.TorqueComponent} <a
54 * href="http://avalon.apache.org/">Avalon</a> implementation leverage
55 * this class.
56 *
57 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
58 * @author <a href="mailto:magnus@handtolvur.is">Magn�s ��r Torfason</a>
59 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
60 * @author <a href="mailto:Rafal.Krzewski@e-point.pl">Rafal Krzewski</a>
61 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
62 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
63 * @author <a href="mailto:kschrader@karmalab.org">Kurt Schrader</a>
64 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
65 * @version $Id: TorqueInstance.java 1375892 2012-08-22 04:11:33Z tfischer $
66 */
67 public class TorqueInstance
68 {
69 /** Logging */
70 private static Log log = LogFactory.getLog(TorqueInstance.class);
71
72 /** A constant for <code>default</code>. */
73 private static final String DEFAULT_NAME = "default";
74
75 /** The db name that is specified as the default in the property file */
76 private String defaultDBName = null;
77
78 /**
79 * The Map which contains all known databases. All iterations over the map
80 * and other operations where the databaase map needs to stay
81 * in a defined state must be synchronized to this map.
82 */
83 private final Map<String, Database> databases
84 = Collections.synchronizedMap(new HashMap<String, Database>());
85
86 /** A repository of Manager instances. */
87 private Map<String, AbstractBaseManager<?>> managers;
88
89 /** A repository of idBroker instances. */
90 private final Set<IDBroker> idBrokers = new HashSet<IDBroker>();
91
92 /** Torque-specific configuration. */
93 private Configuration conf;
94
95 /** Flag to set to true once this class has been initialized */
96 private boolean isInit = false;
97
98 /**
99 * A flag which indicates whether the DataSourceFactory in the database
100 * named <code>DEFAULT</code> is a reference to another
101 * DataSourceFactory. This is important to know when closing the
102 * DataSourceFactories on shutdown();
103 */
104 private boolean defaultDsfIsReference = false;
105
106 /**
107 * Creates a new instance with default configuration.
108 *
109 * @see #resetConfiguration()
110 */
111 public TorqueInstance()
112 {
113 resetConfiguration();
114 }
115
116 /**
117 * Initializes this instance of Torque.
118 *
119 * @throws TorqueException if Torque is already initialized
120 * or an error during initialization occurs.
121 */
122 private synchronized void initialize() throws TorqueException
123 {
124 log.debug("initialize()");
125
126 if (isInit)
127 {
128 throw new TorqueException(
129 "Multiple initializations of Torque attempted");
130 }
131
132 if (conf == null || conf.isEmpty())
133 {
134 throw new TorqueException("Torque cannot be initialized without "
135 + "a valid configuration. Please check the log files "
136 + "for further details.");
137 }
138
139 initTransactionManager(conf);
140 initDefaultDbName(conf);
141 initDataSourceFactories(conf);
142 initSchemata(conf);
143 initAdapters(conf);
144
145 // As there might be a default database configured
146 // to map "default" onto an existing datasource, we
147 // must check, whether there _is_ really an entry for
148 // the "default" in the dsFactoryMap or not. If it is
149 // not, then add a dummy entry for the "default"
150 //
151 // Without this, you can't actually access the "default"
152 // data-source, even if you have an entry like
153 //
154 // database.default = bookstore
155 //
156 // in your Torque.properties
157 //
158
159 {
160 Database defaultDatabase = databases.get(defaultDBName);
161 Database databaseInfoForKeyDefault
162 = getOrCreateDatabase(DEFAULT_NAME);
163 if ((!defaultDBName.equals(DEFAULT_NAME))
164 && databaseInfoForKeyDefault.getDataSourceFactory() == null)
165 {
166 log.debug("Adding the DatasourceFactory and DatabaseAdapter "
167 + "from database "
168 + defaultDBName
169 + " onto database " + DEFAULT_NAME);
170 databaseInfoForKeyDefault.setDataSourceFactory(
171 defaultDatabase.getDataSourceFactory());
172 databaseInfoForKeyDefault.setAdapter(
173 defaultDatabase.getAdapter());
174
175 this.defaultDsfIsReference = true;
176 }
177 }
178
179 // setup manager mappings
180 initManagerMappings(conf);
181 startIdBrokers();
182
183 isInit = true;
184 }
185
186
187 /**
188 * Initializes the transaction manager.
189 *
190 * @param conf the configuration representing the torque section.
191 * of the properties file.
192 *
193 * @throws TorqueException if the transaction manger configuration
194 * is invalid.
195 */
196 private void initTransactionManager(Configuration conf)
197 throws TorqueException
198 {
199 log.debug("initTransactionManager(" + conf + ")");
200
201 String transactionManagerClassName =
202 conf.getString(Torque.TRANSACTION_MANAGER_KEY);
203 TransactionManager transactionManager;
204 if (StringUtils.isEmpty(transactionManagerClassName))
205 {
206 if (log.isTraceEnabled())
207 {
208 log.trace("Configuration key " + Torque.TORQUE_KEY + "."
209 + Torque.TRANSACTION_MANAGER_KEY
210 + " not set, using default transaction manager "
211 + TransactionManagerImpl.class.getName());
212 }
213 transactionManager = new TransactionManagerImpl();
214 }
215 else
216 {
217 try
218 {
219 Class<?> transactionManagerClass
220 = Class.forName(transactionManagerClassName);
221 transactionManager = (TransactionManager)
222 transactionManagerClass.newInstance();
223 if (log.isTraceEnabled())
224 {
225 log.trace("Using transaction manager "
226 + transactionManager.getClass().getName());
227 }
228 }
229 catch (Exception e)
230 {
231 log.error("Error handling transaction manager configuration",
232 e);
233 throw new TorqueException(e);
234 }
235 }
236 Transaction.setTransactionManager(transactionManager);
237 }
238
239
240 /**
241 * Initializes the name of the default database and
242 * associates the database with the name <code>DEFAULT_NAME</code>
243 * to the default database.
244 *
245 * @param conf the configuration representing the torque section.
246 * of the properties file.
247 *
248 * @throws TorqueException if the appropriate key is not set.
249 */
250 private void initDefaultDbName(Configuration conf)
251 throws TorqueException
252 {
253 log.debug("initDefaultDbName(" + conf + ")");
254
255 // Determine default database name.
256 defaultDBName =
257 conf.getString(
258 Torque.DATABASE_KEY
259 + "."
260 + Torque.DEFAULT_KEY);
261 if (defaultDBName == null)
262 {
263 String error = "Invalid configuration: Key "
264 + Torque.TORQUE_KEY
265 + "."
266 + Torque.DATABASE_KEY
267 + "."
268 + Torque.DEFAULT_KEY
269 + " not set";
270 log.error(error);
271 throw new TorqueException(error);
272 }
273 }
274
275 /**
276 * Reads the adapter settings from the configuration and
277 * assigns the appropriate database adapters and Id Generators
278 * to the databases.
279 *
280 * @param conf the Configuration representing the torque section of the
281 * properties file.
282 *
283 * @throws TorqueException Any exceptions caught during processing will be
284 * rethrown wrapped into a TorqueException.
285 */
286 private void initAdapters(Configuration conf)
287 throws TorqueException
288 {
289 log.debug("initAdapters(" + conf + ")");
290
291 Configuration c = conf.subset(Torque.DATABASE_KEY);
292 if (c == null || c.isEmpty())
293 {
294 String error = "Invalid configuration : "
295 + "No keys starting with "
296 + Torque.TORQUE_KEY
297 + "."
298 + Torque.DATABASE_KEY
299 + " found in configuration";
300 log.error(error);
301 throw new TorqueException(error);
302 }
303
304 try
305 {
306 for (Iterator<?> it = c.getKeys(); it.hasNext();)
307 {
308 String key = (String) it.next();
309 if (key.endsWith(Adapter.ADAPTER_KEY)
310 || key.endsWith(Adapter.DRIVER_KEY))
311 {
312 String adapterKey = c.getString(key);
313 String handle = key.substring(0, key.indexOf('.'));
314
315 Database database = getOrCreateDatabase(handle);
316 Adapter adapter = null;
317
318 if (StringUtils.equals(
319 Adapter.AUTODETECT_ADAPTER,
320 adapterKey))
321 {
322 Connection con = null;
323 try
324 {
325 con = database.getDataSourceFactory()
326 .getDataSource().getConnection();
327 adapter = AdapterFactory.autoDetectAdapter(con);
328 }
329 catch (SQLException e)
330 {
331 log.error(
332 "Could not get product information from JDBC",
333 e);
334 }
335 finally
336 {
337 closeConnection(con);
338 }
339 }
340 else
341 {
342 adapter = AdapterFactory.create(adapterKey);
343 }
344
345 // Not supported, try manually defined adapter class
346 if (adapter == null)
347 {
348 String adapterClassName = c.getString(
349 key + "." + adapterKey + ".className", null);
350 adapter = AdapterFactory.create(
351 adapterKey,
352 adapterClassName);
353 }
354
355 // register the adapter for this name
356 database.setAdapter(adapter);
357 log.debug("Adding " + adapterKey + " -> "
358 + handle + " as Adapter");
359
360 // add Id generators
361 for (IDMethod idMethod
362 : IDGeneratorFactory.ID_GENERATOR_METHODS)
363 {
364 database.addIdGenerator(
365 idMethod,
366 IDGeneratorFactory.create(adapter, handle));
367 }
368 }
369 }
370 }
371 catch (InstantiationException e)
372 {
373 log.error("Error creating a database adapter instance", e);
374 throw new TorqueException(e);
375 }
376
377 // check that at least the default database has got an adapter.
378 Database defaultDatabase
379 = databases.get(getDefaultDB());
380 if (defaultDatabase == null
381 || defaultDatabase.getAdapter() == null)
382 {
383 String error = "Invalid configuration : "
384 + "No adapter definition found for default DB "
385 + "An adapter must be defined under "
386 + Torque.TORQUE_KEY
387 + "."
388 + Torque.DATABASE_KEY
389 + "."
390 + getDefaultDB()
391 + "."
392 + Adapter.ADAPTER_KEY;
393 log.error(error);
394 throw new TorqueException(error);
395 }
396 }
397
398 /**
399 * Reads the settings for the DataSourceFactories from the
400 * configuration and creates and/or configures the DataSourceFactories
401 * and the database objects.
402 * If no DataSorceFactory is assigned to the database with the name
403 * <code>DEFAULT_NAME</code>, a reference to the DataSourceFactory
404 * of the default database is made from the database with the name
405 * <code>DEFAULT_NAME</code>.
406 *
407 * @param conf the Configuration representing the properties file.
408 *
409 * @throws TorqueException Any exceptions caught during processing will be
410 * rethrown wrapped into a TorqueException.
411 */
412 private void initDataSourceFactories(Configuration conf)
413 throws TorqueException
414 {
415 log.debug("initDataSourceFactories(" + conf + ")");
416
417 Configuration c = conf.subset(DataSourceFactory.DSFACTORY_KEY);
418 if (c == null || c.isEmpty())
419 {
420 String error = "Invalid configuration: "
421 + "No keys starting with "
422 + Torque.TORQUE_KEY
423 + "."
424 + DataSourceFactory.DSFACTORY_KEY
425 + " found in configuration";
426 log.error(error);
427 throw new TorqueException(error);
428 }
429
430 // read dsfactory config (may contain schema)
431 try
432 {
433 for (Iterator<?> it = c.getKeys(); it.hasNext();)
434 {
435 String key = (String) it.next();
436 if (key.endsWith(DataSourceFactory.FACTORY_KEY))
437 {
438 String classname = c.getString(key);
439 String handle = key.substring(0, key.indexOf('.'));
440 log.debug("handle: " + handle
441 + " DataSourceFactory: " + classname);
442 Class<?> dsfClass = Class.forName(classname);
443 DataSourceFactory dsf =
444 (DataSourceFactory) dsfClass.newInstance();
445 Configuration subConf = c.subset(handle);
446 dsf.initialize(subConf);
447
448 Database database = getOrCreateDatabase(handle);
449 database.setDataSourceFactory(dsf);
450
451 // deprecated method of schema configuration
452 // TODO: remove in Torque 4.1
453 String schema = subConf.getString(Torque.SCHEMA_KEY, null);
454 if (!StringUtils.isEmpty(schema))
455 {
456 log.warn("Defining the schema in the dsfactory "
457 + "is deprecated, please configure it "
458 + "via the config key "
459 + "torque.database.${databasename}.schema");
460 }
461 database.setSchema(schema);
462 }
463 }
464 }
465 catch (RuntimeException e)
466 {
467 log.error("Error reading DataSourceFactory configuration", e);
468 throw new TorqueRuntimeException(e);
469 }
470 catch (Exception e)
471 {
472 log.error("Error reading DataSourceFactory configuration", e);
473 throw new TorqueException(e);
474 }
475
476 Database defaultDatabase
477 = databases.get(defaultDBName);
478 if (defaultDatabase == null
479 || defaultDatabase.getDataSourceFactory() == null)
480 {
481 String error = "Invalid configuration : "
482 + "No DataSourceFactory definition for default DB found. "
483 + "A DataSourceFactory must be defined under the key"
484 + Torque.TORQUE_KEY
485 + "."
486 + DataSourceFactory.DSFACTORY_KEY
487 + "."
488 + defaultDBName
489 + "."
490 + DataSourceFactory.FACTORY_KEY;
491 log.error(error);
492 throw new TorqueException(error);
493 }
494 }
495
496 /**
497 * Reads the schema configuration from the database definitions in
498 * the configuration and assigns the defined schemata to the databases.
499 *
500 * @param conf the Configuration representing the properties file.
501 *
502 * @throws TorqueException Any exceptions caught during processing will be
503 * rethrown wrapped into a TorqueException.
504 */
505 private void initSchemata(Configuration conf)
506 throws TorqueException
507 {
508 log.debug("initSchemata(" + conf + ")");
509
510 // read schema configuration from database setting
511 Configuration c = conf.subset(Torque.DATABASE_KEY);
512 if (c == null || c.isEmpty())
513 {
514 String error = "Invalid configuration: "
515 + "No keys starting with "
516 + Torque.TORQUE_KEY
517 + "."
518 + Torque.DATABASE_KEY
519 + " found in configuration";
520 log.error(error);
521 throw new TorqueException(error);
522 }
523 try
524 {
525 for (Iterator<?> it = c.getKeys(); it.hasNext();)
526 {
527 String key = (String) it.next();
528 int indexOfDot = key.indexOf('.');
529 if (indexOfDot == -1)
530 {
531 continue;
532 }
533 String handle = key.substring(0, indexOfDot);
534
535 log.debug("database handle: " + handle);
536 Configuration subConf = c.subset(handle);
537
538 Database database = getOrCreateDatabase(handle);
539
540 String schema = subConf.getString(Torque.SCHEMA_KEY, null);
541 // check database schema because schema may have already been
542 // set via the dsfactory
543 if (StringUtils.isEmpty(schema))
544 {
545 schema = database.getSchema();
546 }
547 if (StringUtils.isEmpty(schema))
548 {
549 schema = conf.getString(
550 Torque.DEFAULT_SCHEMA_KEY,
551 null);
552 }
553 database.setSchema(schema);
554 }
555 }
556 catch (RuntimeException e)
557 {
558 log.error("Error reading DataSourceFactory configuration", e);
559 throw new TorqueRuntimeException(e);
560 }
561 catch (Exception e)
562 {
563 log.error("Error reading DataSourceFactory configuration", e);
564 throw new TorqueException(e);
565 }
566
567 }
568
569 /**
570 * Initialization of Torque with a path to a properties file.
571 *
572 * @param configFile The absolute path to the configuration file.
573 *
574 * @throws TorqueException Any exceptions caught during processing will be
575 * rethrown wrapped into a TorqueException.
576 */
577 public void init(String configFile)
578 throws TorqueException
579 {
580 log.debug("init(" + configFile + ")");
581 try
582 {
583 Configuration configuration
584 = new PropertiesConfiguration(configFile);
585
586 log.debug("Config Object is " + configuration);
587 init(configuration);
588 }
589 catch (ConfigurationException e)
590 {
591 throw new TorqueException(e);
592 }
593 }
594
595 /**
596 * Initialization of Torque with a Configuration object.
597 *
598 * @param conf The Torque configuration.
599 *
600 * @throws TorqueException Any exceptions caught during processing will be
601 * rethrown wrapped into a TorqueException.
602 */
603 public synchronized void init(Configuration conf)
604 throws TorqueException
605 {
606 log.debug("init(" + conf + ")");
607 setConfiguration(conf);
608 initialize();
609 }
610
611
612 /**
613 * Creates a mapping between classes and their manager classes.
614 *
615 * The mapping is built according to settings present in
616 * properties file. The entries should have the
617 * following form:
618 *
619 * <pre>
620 * torque.managed_class.com.mycompany.Myclass.manager= \
621 * com.mycompany.MyManagerImpl
622 * services.managed_class.com.mycompany.Myotherclass.manager= \
623 * com.mycompany.MyOtherManagerImpl
624 * </pre>
625 *
626 * <br>
627 *
628 * Generic ServiceBroker provides no Services.
629 *
630 * @param conf the Configuration representing the properties file
631 * @throws TorqueException Any exceptions caught during processing will be
632 * rethrown wrapped into a TorqueException.
633 */
634 protected synchronized void initManagerMappings(Configuration conf)
635 throws TorqueException
636 {
637 int pref = Torque.MANAGER_PREFIX.length();
638 int suff = Torque.MANAGER_SUFFIX.length();
639
640 for (Iterator<?> it = conf.getKeys(); it.hasNext();)
641 {
642 String key = (String) it.next();
643
644 if (key.startsWith(Torque.MANAGER_PREFIX)
645 && key.endsWith(Torque.MANAGER_SUFFIX))
646 {
647 String managedClassKey = key.substring(pref,
648 key.length() - suff);
649 if (!managers.containsKey(managedClassKey))
650 {
651 String managerClass = conf.getString(key);
652 log.info("Added Manager for Class: " + managedClassKey
653 + " -> " + managerClass);
654 try
655 {
656 initManager(managedClassKey, managerClass);
657 }
658 catch (TorqueException e)
659 {
660 // the exception thrown here seems to disappear.
661 // At least when initialized by Turbine, should find
662 // out why, but for now make sure it is noticed.
663 log.error("", e);
664 e.printStackTrace();
665 throw e;
666 }
667 }
668 }
669 }
670 }
671
672 /**
673 * Initialize a manager
674 *
675 * @param name name of the manager
676 * @param className name of the manager class
677 * @throws TorqueException Any exceptions caught during processing will be
678 * rethrown wrapped into a TorqueException.
679 */
680 private synchronized void initManager(String name, String className)
681 throws TorqueException
682 {
683 AbstractBaseManager<?> manager = managers.get(name);
684
685 if (manager == null)
686 {
687 if (className != null && className.length() != 0)
688 {
689 try
690 {
691 manager = (AbstractBaseManager<?>)
692 Class.forName(className).newInstance();
693 managers.put(name, manager);
694 }
695 catch (Exception e)
696 {
697 throw new TorqueException("Could not instantiate "
698 + "manager associated with class: "
699 + name, e);
700 }
701 }
702 }
703 }
704
705 /**
706 * Starts all registered IdBrokers.
707 */
708 private void startIdBrokers()
709 {
710 for (IDBroker idBroker : idBrokers)
711 {
712 idBroker.start();
713 }
714 }
715
716 /**
717 * Determine whether Torque has already been initialized.
718 *
719 * @return true if Torque is already initialized
720 */
721 public boolean isInit()
722 {
723 return isInit;
724 }
725
726 /**
727 * Sets the configuration for Torque and all dependencies.
728 * The prefix <code>TORQUE_KEY</code> will be removed from the
729 * configuration keys for the provided configuration.
730 *
731 * @param conf the Configuration.
732 *
733 * @throws TorqueException if the configuration does not contain
734 * any keys starting with <code>Torque.TORQUE_KEY</code>.
735 */
736 public void setConfiguration(Configuration conf)
737 throws TorqueException
738 {
739 log.debug("setConfiguration(" + conf + ")");
740
741 Configuration subConf = conf.subset(Torque.TORQUE_KEY);
742 if (subConf == null || subConf.isEmpty())
743 {
744 String error = ("Invalid configuration. No keys starting with "
745 + Torque.TORQUE_KEY
746 + " found in configuration");
747 log.error(error);
748 throw new TorqueException(error);
749 }
750 this.conf = subConf;
751 }
752
753 /**
754 * Get the configuration for this component.
755 *
756 * @return the Configuration
757 */
758 public Configuration getConfiguration()
759 {
760 log.debug("getConfiguration() = " + conf);
761 return conf;
762 }
763
764 /**
765 * This method returns a Manager for the given name.
766 *
767 * @param name name of the manager
768 * @return a Manager
769 */
770 public <T extends AbstractBaseManager<? extends Persistent>> T getManager(String name)
771 {
772 @SuppressWarnings("unchecked")
773 T m = (T) managers.get(name);
774 if (m == null)
775 {
776 log.error("No configured manager for key " + name + ".");
777 }
778 return m;
779 }
780
781 /**
782 * This methods returns either the Manager from the configuration file,
783 * or the default one provided by the generated code.
784 *
785 * @param name name of the manager.
786 * @param defaultClassName the class to use if name has not been configured.
787 *
788 * @return a Manager
789 */
790 @SuppressWarnings("unchecked")
791 public <T extends AbstractBaseManager<? extends Persistent>> T getManager(String name,
792 String defaultClassName)
793 {
794 T m = (T) managers.get(name);
795 if (m == null)
796 {
797 log.debug("Added late Manager mapping for Class: "
798 + name + " -> " + defaultClassName);
799
800 try
801 {
802 initManager(name, defaultClassName);
803 }
804 catch (TorqueException e)
805 {
806 log.error(e.getMessage(), e);
807 }
808
809 // Try again now that the default manager should be in the map
810 m = (T) managers.get(name);
811 }
812
813 return m;
814 }
815
816 /**
817 * Shuts down Torque.
818 *
819 * This method halts the IDBroker's daemon thread in all of
820 * the DatabaseMap's. It also closes all SharedPoolDataSourceFactories
821 * and PerUserPoolDataSourceFactories initialized by Torque.
822 *
823 * @throws TorqueException if a DataSourceFactory could not be closed
824 * cleanly. Only the first exception is rethrown, any following
825 * exceptions are logged but ignored.
826 */
827 public synchronized void shutdown()
828 throws TorqueException
829 {
830 for (IDBroker idBroker : idBrokers)
831 {
832 idBroker.stop();
833 // do not remove idbrokers because they will not be
834 // re-registered on a new startup.
835 }
836
837 // shut down the cache managers
838 for (Iterator<Map.Entry<String, AbstractBaseManager<?>>> it
839 = managers.entrySet().iterator(); it.hasNext();)
840 {
841 Map.Entry<String, AbstractBaseManager<?>> mentry
842 = it.next();
843
844 AbstractBaseManager<?> manager = mentry.getValue();
845 manager.dispose();
846 it.remove();
847 }
848
849 // shut down the data source factories
850 TorqueException exception = null;
851 synchronized (databases)
852 {
853 for (Map.Entry<String, Database> databaseMapEntry
854 : databases.entrySet())
855 {
856 Object databaseKey = databaseMapEntry.getKey();
857 Database database = databaseMapEntry.getValue();
858 if (DEFAULT_NAME.equals(databaseKey) && defaultDsfIsReference)
859 {
860 // the DataSourceFactory of the database with the name
861 // DEFAULT_NAME is just a reference to another entry.
862 // Do not close because this leads to closing
863 // the same DataSourceFactory twice.
864 database.setDataSourceFactory(null);
865 continue;
866 }
867
868 try
869 {
870 DataSourceFactory dataSourceFactory
871 = database.getDataSourceFactory();
872 if (dataSourceFactory != null)
873 {
874 dataSourceFactory.close();
875 database.setDataSourceFactory(null);
876 }
877 }
878 catch (TorqueException e)
879 {
880 log.error("Error while closing the DataSourceFactory "
881 + databaseKey,
882 e);
883 if (exception == null)
884 {
885 exception = e;
886 }
887 }
888 }
889 }
890 if (exception != null)
891 {
892 throw exception;
893 }
894 resetConfiguration();
895 }
896
897 /**
898 * Resets some internal configuration variables to their defaults.
899 */
900 private void resetConfiguration()
901 {
902 managers = new HashMap<String, AbstractBaseManager<?>>();
903 isInit = false;
904 }
905
906 /**
907 * Returns the database map information for the default db.
908 *
909 * @return the requested DatabaseMap, not null.
910 *
911 * @throws TorqueException if Torque is not initialized.
912 */
913 public DatabaseMap getDatabaseMap()
914 throws TorqueException
915 {
916 String name = getDefaultDB();
917 if (name == null)
918 {
919 throw new TorqueException("Torque is not initialized");
920 }
921 return getDatabaseMap(getDefaultDB());
922 }
923
924 /**
925 * Returns the database map information for the given database name.
926 *
927 * @param name The name of the database corresponding to the
928 * <code>DatabaseMap</code> to retrieve, or null
929 * for the default database.
930 *
931 * @return The named <code>DatabaseMap</code>, not null.
932 *
933 * @throws TorqueException if Torque is not initialized and name is null.
934 */
935 public DatabaseMap getDatabaseMap(String name)
936 throws TorqueException
937 {
938 if (name == null)
939 {
940 if (!Torque.isInit())
941 {
942 throw new TorqueException("Torque is not initialized");
943 }
944 name = getDefaultDB();
945 }
946 Database database = getOrCreateDatabase(name);
947 return database.getDatabaseMap();
948 }
949
950 /**
951 * Registers an id broker. If Torque is already initialized,
952 * the id broker is started. If Torque is not initialized,
953 * the id broker will be started on initialization.
954 *
955 * @param idBroker the id broker to register, not null.
956 *
957 * @throws NullPointerException if idBroker is null.
958 */
959 public void registerIDBroker(IDBroker idBroker)
960 {
961 idBrokers.add(idBroker);
962 if (isInit())
963 {
964 idBroker.start();
965 }
966 }
967
968 /**
969 * This method returns a Connection from the default pool.
970 *
971 * @return The requested connection, never null.
972 *
973 * @throws TorqueException Any exceptions caught during processing will be
974 * rethrown wrapped into a TorqueException.
975 */
976 public Connection getConnection()
977 throws TorqueException
978 {
979 return getConnection(getDefaultDB());
980 }
981
982 /**
983 * Returns a database connection to the database with the key
984 * <code>name</code>.
985 *
986 * @param name The database name.
987 *
988 * @return a database connection to the named database, never null.
989 *
990 * @throws TorqueException if Torque is not initialized,
991 * if no DataSourceFactory is configured for the
992 * named database, the connection information is wrong, or the
993 * connection cannot be returned for any other reason.
994 */
995 public Connection getConnection(String name)
996 throws TorqueException
997 {
998 if (!isInit())
999 {
1000 throw new TorqueException("Torque is not initialized");
1001 }
1002 try
1003 {
1004 return getDatabase(name)
1005 .getDataSourceFactory()
1006 .getDataSource()
1007 .getConnection();
1008 }
1009 catch (SQLException se)
1010 {
1011 throw new TorqueException(se);
1012 }
1013 }
1014
1015 /**
1016 * Returns the DataSourceFactory for the database with the name
1017 * <code>name</code>.
1018 *
1019 * @param name The name of the database to get the DSF for.
1020 *
1021 * @return A DataSourceFactory object, never null.
1022 *
1023 * @throws TorqueException if Torque is not initialized, or
1024 * no DatasourceFactory is configured for the given name.
1025 */
1026 public DataSourceFactory getDataSourceFactory(String name)
1027 throws TorqueException
1028 {
1029 if (!isInit())
1030 {
1031 throw new TorqueException("Torque is not initialized");
1032 }
1033
1034 Database database = getDatabase(name);
1035
1036 DataSourceFactory dsf = null;
1037 if (database != null)
1038 {
1039 dsf = database.getDataSourceFactory();
1040 }
1041
1042 if (dsf == null)
1043 {
1044 throw new TorqueException(
1045 "There was no DataSourceFactory "
1046 + "configured for the connection " + name);
1047 }
1048
1049 return dsf;
1050 }
1051
1052 /**
1053 * This method returns a Connection using the given parameters.
1054 * You should only use this method if you need user based access to the
1055 * database!
1056 *
1057 * @param name The database name.
1058 * @param username The name of the database user.
1059 * @param password The password of the database user.
1060 *
1061 * @return A Connection to the named database.
1062 *
1063 * @throws TorqueException Any exceptions caught during processing will be
1064 * rethrown wrapped into a TorqueException.
1065 */
1066 public Connection getConnection(String name, String username,
1067 String password)
1068 throws TorqueException
1069 {
1070 if (!isInit())
1071 {
1072 throw new TorqueException("Torque is not initialized");
1073 }
1074 try
1075 {
1076 return getDataSourceFactory(name)
1077 .getDataSource().getConnection(username, password);
1078 }
1079 catch (SQLException se)
1080 {
1081 throw new TorqueException(se);
1082 }
1083 }
1084
1085 /**
1086 * Returns the database adapter for a specific database.
1087 *
1088 * @param name the database name, or null for the default db.
1089 *
1090 * @return The corresponding database adapter, or null if no database
1091 * adapter is defined for the given database.
1092 *
1093 * @throws TorqueException if Torque is not initialized.
1094 *
1095 * @deprecated use getAdapter(String) instead.
1096 * This method will be removed in a future version of Torque.
1097 */
1098 @Deprecated
1099 public Adapter getDB(String name) throws TorqueException
1100 {
1101 if (!isInit())
1102 {
1103 throw new TorqueException("Torque is not initialized");
1104 }
1105 Database database = getDatabase(name);
1106 if (database == null)
1107 {
1108 return null;
1109 }
1110 return database.getAdapter();
1111 }
1112
1113 /**
1114 * Returns the database adapter for a specific database.
1115 *
1116 * @param name the database name, or null for the default db.
1117 *
1118 * @return The corresponding database adapter, or null if no database
1119 * adapter is defined for the given database.
1120 *
1121 * @throws TorqueException if Torque is not initialized.
1122 */
1123 public Adapter getAdapter(String name) throws TorqueException
1124 {
1125 if (!isInit())
1126 {
1127 throw new TorqueException("Torque is not initialized");
1128 }
1129 Database database = getDatabase(name);
1130 if (database == null)
1131 {
1132 return null;
1133 }
1134 return database.getAdapter();
1135 }
1136
1137 ///////////////////////////////////////////////////////////////////////////
1138
1139 /**
1140 * Returns the name of the default database.
1141 *
1142 * @return name of the default DB, or null if Torque is not initialized yet
1143 */
1144 public String getDefaultDB()
1145 {
1146 return defaultDBName;
1147 }
1148
1149 /**
1150 * Closes a connection.
1151 *
1152 * @param con A Connection to close.
1153 */
1154 public void closeConnection(Connection con)
1155 {
1156 if (con != null)
1157 {
1158 try
1159 {
1160 con.close();
1161 }
1162 catch (SQLException e)
1163 {
1164 log.error("Error occured while closing connection.", e);
1165 }
1166 }
1167 }
1168
1169 /**
1170 * Sets the current schema for a database connection
1171 *
1172 * @param name The database name, not null.
1173 * @param schema The current schema name.
1174 *
1175 * @throws NullPointerException if databaseName is null.
1176 */
1177 public void setSchema(String name, String schema)
1178 {
1179 getOrCreateDatabase(name).setSchema(schema);
1180 }
1181
1182 /**
1183 * This method returns the current schema for a database connection
1184 *
1185 * @param name The database name.
1186 *
1187 * @return The current schema name. Null means, no schema has been set
1188 * or no database with the given name exists.
1189 *
1190 * @throws TorqueException if Torque is not yet initialized.
1191 */
1192 public String getSchema(String name)
1193 throws TorqueException
1194 {
1195 if (!isInit())
1196 {
1197 throw new TorqueException("Torque is not initialized");
1198 }
1199 Database database = getDatabase(name);
1200 if (database == null)
1201 {
1202 return null;
1203 }
1204 return database.getSchema();
1205 }
1206
1207 /**
1208 * Returns the database for the key <code>databaseName</code>.
1209 *
1210 * @param databaseName the key to get the database for,
1211 * or null for the default database.
1212 *
1213 * @return the database for the specified key, or null if the database
1214 * does not exist.
1215 *
1216 * @throws TorqueException if Torque is not yet initialized.
1217 */
1218 public Database getDatabase(String databaseName) throws TorqueException
1219 {
1220 if (!isInit())
1221 {
1222 throw new TorqueException("Torque is not initialized.");
1223 }
1224 if (databaseName == null)
1225 {
1226 databaseName = getDefaultDB();
1227 }
1228 return databases.get(databaseName);
1229 }
1230
1231 /**
1232 * Returns a Map containing all Databases registered to Torque.
1233 * The key of the Map is the name of the database, and the value is the
1234 * database instance. <br/>
1235 * Note that in the very special case where a new database which
1236 * is not configured in Torque's configuration gets known to Torque
1237 * at a later time, the returned map may change, and there is no way to
1238 * protect you against this.
1239 *
1240 * @return a Map containing all Databases known to Torque, never null.
1241 *
1242 * @throws TorqueException if Torque is not yet initialized.
1243 */
1244 public Map<String, Database> getDatabases() throws TorqueException
1245 {
1246 if (!isInit())
1247 {
1248 throw new TorqueException("Torque is not initialized.");
1249 }
1250 return Collections.unmodifiableMap(databases);
1251 }
1252
1253 /**
1254 * Returns the database for the key <code>databaseName</code>.
1255 * If no database is associated to the specified key,
1256 * a new database is created, mapped to the specified key, and returned.
1257 *
1258 * @param databaseName the key to get the database for, not null.
1259 *
1260 * @return the database associated with specified key, or the newly created
1261 * database, never null.
1262 *
1263 * @throws NullPointerException if databaseName is null.
1264 */
1265 public Database getOrCreateDatabase(String databaseName)
1266 {
1267 if (databaseName == null)
1268 {
1269 throw new NullPointerException("databaseName is null");
1270 }
1271 synchronized (databases)
1272 {
1273 Database result = databases.get(databaseName);
1274 if (result == null)
1275 {
1276 result = new Database(databaseName);
1277 databases.put(databaseName, result);
1278 }
1279 return result;
1280 }
1281 }
1282 }