View Javadoc

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.ArrayList;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
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.logging.Log;
35  import org.apache.commons.logging.LogFactory;
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.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   * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
60   * @version $Id: TorqueInstance.java 476550 2006-11-18 16:08:37Z tfischer $
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 = null;
72  
73      /***
74       * The Map which contains all known databases. All iterations over the map
75       * and other operations where the databaase map needs to stay
76       * in a defined state must be synchronized to this map.
77       */
78      private Map databases = Collections.synchronizedMap(new HashMap());
79  
80      /*** A repository of Manager instances. */
81      private Map managers;
82  
83      /*** Torque-specific configuration. */
84      private Configuration conf;
85  
86      /*** flag to set to true once this class has been initialized */
87      private boolean isInit = false;
88  
89      /***
90       * a flag which indicates whether the DataSourceFactory in the database
91       * named <code>DEFAULT</code> is a reference to another
92       * DataSourceFactory. This is important to know when closing the
93       * DataSourceFactories on shutdown();
94       */
95      private boolean defaultDsfIsReference = false;
96  
97      /***
98       * Store mapbuilder classnames for peers that have been referenced prior
99       * to Torque being initialized.  This can happen if torque om/peer objects
100      * are serialized then unserialized prior to Torque being reinitialized.
101      * This condition exists in a normal catalina restart.
102      */
103     private List mapBuilders = null;
104 
105     /***
106      * Creates a new instance with default configuration.
107      *
108      * @see #resetConfiguration()
109      */
110     public TorqueInstance()
111     {
112         resetConfiguration();
113     }
114 
115     /***
116      * Initializes this instance of Torque.
117      *
118      * @see org.apache.stratum.lifecycle.Initializable
119      * @throws TorqueException Any exceptions caught during processing will be
120      *         rethrown wrapped into a TorqueException.
121      */
122     private synchronized void initialize() throws TorqueException
123     {
124         log.debug("initialize()");
125 
126         if (isInit)
127         {
128             log.debug("Multiple initializations of Torque attempted");
129             return;
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         // Now that we have dealt with processing the log4j properties
140         // that may be contained in the configuration we will make the
141         // configuration consist only of the remain torque specific
142         // properties that are contained in the configuration. First
143         // look for properties that are in the "torque" namespace.
144 
145         Configuration subConf = conf.subset(Torque.TORQUE_KEY);
146         if (subConf == null || subConf.isEmpty())
147         {
148             String error = ("Invalid configuration. No keys starting with "
149                     + Torque.TORQUE_KEY
150                     + " found in configuration");
151             log.error(error);
152             throw new TorqueException(error);
153         }
154         setConfiguration(subConf);
155 
156         initDefaultDbName(conf);
157         initAdapters(conf);
158         initDataSourceFactories(conf);
159 
160         for (Iterator i = mapBuilders.iterator(); i.hasNext();)
161         {
162             //this will add any maps in this builder to the proper database map
163             BasePeer.getMapBuilder((String) i.next());
164         }
165         // any further mapBuilders will be called/built on demand
166         mapBuilders = null;
167 
168         // setup manager mappings
169         initManagerMappings(conf);
170 
171         isInit = true;
172     }
173 
174 
175     /***
176      * Initializes the name of the default database and
177      * associates the database with the name <code>DEFAULT_NAME</code>
178      * to the default database.
179      *
180      * @param conf the configuration representing the torque section.
181      *        of the properties file.
182      * @throws TorqueException if the appropriate key is not set.
183      */
184     private void initDefaultDbName(Configuration conf)
185             throws TorqueException
186     {
187         // Determine default database name.
188         defaultDBName =
189                 conf.getString(
190                         Torque.DATABASE_KEY
191                         + "."
192                         + Torque.DEFAULT_KEY);
193         if (defaultDBName == null)
194         {
195             String error = "Invalid configuration: Key "
196                     + Torque.TORQUE_KEY
197                     + "."
198                     + Torque.DATABASE_KEY
199                     + "."
200                     + Torque.DEFAULT_KEY
201                     + " not set";
202             log.error(error);
203             throw new TorqueException(error);
204         }
205     }
206 
207     /***
208      * Reads the adapter settings from the configuration and
209      * assigns the appropriate database adapters and Id Generators
210      * to the databases.
211      *
212      * @param conf the Configuration representing the torque section of the
213      *        properties file
214      * @throws TorqueException Any exceptions caught during processing will be
215      *         rethrown wrapped into a TorqueException.
216      */
217     private void initAdapters(Configuration conf)
218             throws TorqueException
219     {
220         log.debug("initAdapters(" + conf + ")");
221 
222         Configuration c = conf.subset(Torque.DATABASE_KEY);
223         if (c == null || c.isEmpty())
224         {
225             String error = "Invalid configuration : "
226                     + "No keys starting with "
227                     + Torque.TORQUE_KEY
228                     + "."
229                     + Torque.DATABASE_KEY
230                     + " found in configuration";
231             log.error(error);
232             throw new TorqueException(error);
233         }
234 
235         try
236         {
237             for (Iterator it = c.getKeys(); it.hasNext();)
238             {
239                 String key = (String) it.next();
240                 if (key.endsWith(DB.ADAPTER_KEY)
241                         || key.endsWith(DB.DRIVER_KEY))
242                 {
243                     String adapter = c.getString(key);
244                     String handle = key.substring(0, key.indexOf('.'));
245 
246                     DB db;
247 
248                     db = DBFactory.create(adapter);
249 
250                     // Not supported, try manually defined adapter class
251                     if (db == null)
252                     {
253                         String adapterClassName = c.getString(key + "." + adapter + ".className", null);
254                         db = DBFactory.create(adapter, adapterClassName);
255                     }
256 
257                     Database database = getOrCreateDatabase(handle);
258 
259                     // register the adapter for this name
260                     database.setAdapter(db);
261                     log.debug("Adding " + adapter + " -> "
262                             + handle + " as Adapter");
263 
264                     // add Id generators
265 
266                     // first make sure that the dtabaseMap exists for the name
267                     // as the idGenerators are still stored in the database map
268                     // TODO: change when the idGenerators are stored in the
269                     // database
270                     getDatabaseMap(handle);
271                     for (int i = 0;
272                             i < IDGeneratorFactory.ID_GENERATOR_METHODS.length;
273                             i++)
274                     {
275                         database.addIdGenerator(
276                                 IDGeneratorFactory.ID_GENERATOR_METHODS[i],
277                                 IDGeneratorFactory.create(db, handle));
278                     }
279                 }
280             }
281         }
282         catch (InstantiationException e)
283         {
284             log.error("Error creating a database adapter instance", e);
285             throw new TorqueException(e);
286         }
287         catch (TorqueException e)
288         {
289             log.error("Error reading configuration seeking database "
290                       + "adapters", e);
291             throw new TorqueException(e);
292         }
293 
294         // check that at least the default database has got an adapter.
295         Database defaultDatabase
296                 = (Database) databases.get(Torque.getDefaultDB());
297         if (defaultDatabase == null
298             || defaultDatabase.getAdapter() == null)
299         {
300             String error = "Invalid configuration : "
301                     + "No adapter definition found for default DB "
302                     + "An adapter must be defined under "
303                     + Torque.TORQUE_KEY
304                     + "."
305                     + Torque.DATABASE_KEY
306                     + "."
307                     + Torque.getDefaultDB()
308                     + "."
309                     + DB.ADAPTER_KEY;
310             log.error(error);
311             throw new TorqueException(error);
312         }
313     }
314 
315     /***
316      * Reads the settings for the DataSourceFactories from the configuration
317      * and creates and/or cinfigures the DataSourceFactories for the databases.
318      * If no DataSorceFactory is assigned to the database with the name
319      * <code>DEFAULT_NAME</code>, a reference to the DataSourceFactory
320      * of the default daztabase is made from the database with the name
321      * <code>DEFAULT_NAME</code>.
322      *
323      * @param conf the Configuration representing the properties file
324      * @throws TorqueException Any exceptions caught during processing will be
325      *         rethrown wrapped into a TorqueException.
326      */
327     private void initDataSourceFactories(Configuration conf)
328             throws TorqueException
329     {
330         log.debug("initDataSourceFactories(" + conf + ")");
331 
332         Configuration c = conf.subset(DataSourceFactory.DSFACTORY_KEY);
333         if (c == null || c.isEmpty())
334         {
335             String error = "Invalid configuration: "
336                     + "No keys starting with "
337                     + Torque.TORQUE_KEY
338                     + "."
339                     + DataSourceFactory.DSFACTORY_KEY
340                     + " found in configuration";
341             log.error(error);
342             throw new TorqueException(error);
343         }
344 
345         try
346         {
347             for (Iterator it = c.getKeys(); it.hasNext();)
348             {
349                 String key = (String) it.next();
350                 if (key.endsWith(DataSourceFactory.FACTORY_KEY))
351                 {
352                     String classname = c.getString(key);
353                     String handle = key.substring(0, key.indexOf('.'));
354                     log.debug("handle: " + handle
355                             + " DataSourceFactory: " + classname);
356                     Class dsfClass = Class.forName(classname);
357                     DataSourceFactory dsf =
358                             (DataSourceFactory) dsfClass.newInstance();
359                     dsf.initialize(c.subset(handle));
360 
361                     Database database = getOrCreateDatabase(handle);
362                     database.setDataSourceFactory(dsf);
363                 }
364             }
365         }
366         catch (RuntimeException e)
367         {
368             log.error("Runtime Error reading adapter configuration", e);
369             throw new TorqueRuntimeException(e);
370         }
371         catch (Exception e)
372         {
373             log.error("Error reading adapter configuration", e);
374             throw new TorqueException(e);
375         }
376 
377         Database defaultDatabase
378                 = (Database) databases.get(defaultDBName);
379         if (defaultDatabase == null
380             || defaultDatabase.getDataSourceFactory() == null)
381         {
382             String error = "Invalid configuration : "
383                     + "No DataSourceFactory definition for default DB found. "
384                     + "A DataSourceFactory must be defined under the key"
385                     + Torque.TORQUE_KEY
386                     + "."
387                     + DataSourceFactory.DSFACTORY_KEY
388                     + "."
389                     + defaultDBName
390                     + "."
391                     + DataSourceFactory.FACTORY_KEY;
392             log.error(error);
393             throw new TorqueException(error);
394         }
395 
396         // As there might be a default database configured
397         // to map "default" onto an existing datasource, we
398         // must check, whether there _is_ really an entry for
399         // the "default" in the dsFactoryMap or not. If it is
400         // not, then add a dummy entry for the "default"
401         //
402         // Without this, you can't actually access the "default"
403         // data-source, even if you have an entry like
404         //
405         // database.default = bookstore
406         //
407         // in your Torque.properties
408         //
409 
410         {
411             Database databaseInfoForKeyDefault
412                     = getOrCreateDatabase(DEFAULT_NAME);
413             if ((!defaultDBName.equals(DEFAULT_NAME))
414                 && databaseInfoForKeyDefault.getDataSourceFactory() == null)
415             {
416                 log.debug("Adding the DatasourceFactory from database "
417                         + defaultDBName
418                         + " onto database " + DEFAULT_NAME);
419                 databaseInfoForKeyDefault.setDataSourceFactory(
420                         defaultDatabase.getDataSourceFactory());
421                 this.defaultDsfIsReference = true;
422             }
423         }
424 
425     }
426 
427     /***
428      * Initialization of Torque with a properties file.
429      *
430      * @param configFile The absolute path to the configuration file.
431      * @throws TorqueException Any exceptions caught during processing will be
432      *         rethrown wrapped into a TorqueException.
433      */
434     public void init(String configFile)
435             throws TorqueException
436     {
437         log.debug("init(" + configFile + ")");
438         try
439         {
440             Configuration configuration
441                     = new PropertiesConfiguration(configFile);
442 
443             log.debug("Config Object is " + configuration);
444             init(configuration);
445         }
446         catch (ConfigurationException e)
447         {
448             throw new TorqueException(e);
449         }
450     }
451 
452     /***
453      * Initialization of Torque with a Configuration object.
454      *
455      * @param conf The Torque configuration.
456      * @throws TorqueException Any exceptions caught during processing will be
457      *         rethrown wrapped into a TorqueException.
458      */
459     public synchronized void init(Configuration conf)
460             throws TorqueException
461     {
462         log.debug("init(" + conf + ")");
463         setConfiguration(conf);
464         initialize();
465     }
466 
467 
468     /***
469      * Creates a mapping between classes and their manager classes.
470      *
471      * The mapping is built according to settings present in
472      * properties file.  The entries should have the
473      * following form:
474      *
475      * <pre>
476      * torque.managed_class.com.mycompany.Myclass.manager= \
477      *          com.mycompany.MyManagerImpl
478      * services.managed_class.com.mycompany.Myotherclass.manager= \
479      *          com.mycompany.MyOtherManagerImpl
480      * </pre>
481      *
482      * <br>
483      *
484      * Generic ServiceBroker provides no Services.
485      *
486      * @param conf the Configuration representing the properties file
487      * @throws TorqueException Any exceptions caught during processing will be
488      *         rethrown wrapped into a TorqueException.
489      */
490     protected void initManagerMappings(Configuration conf)
491             throws TorqueException
492     {
493         int pref = Torque.MANAGER_PREFIX.length();
494         int suff = Torque.MANAGER_SUFFIX.length();
495 
496         for (Iterator it = conf.getKeys(); it.hasNext();)
497         {
498             String key = (String) it.next();
499 
500             if (key.startsWith(Torque.MANAGER_PREFIX)
501                     && key.endsWith(Torque.MANAGER_SUFFIX))
502             {
503                 String managedClassKey = key.substring(pref,
504                         key.length() - suff);
505                 if (!managers.containsKey(managedClassKey))
506                 {
507                     String managerClass = conf.getString(key);
508                     log.info("Added Manager for Class: " + managedClassKey
509                             + " -> " + managerClass);
510                     try
511                     {
512                         initManager(managedClassKey, managerClass);
513                     }
514                     catch (TorqueException e)
515                     {
516                         // the exception thrown here seems to disappear.
517                         // At least when initialized by Turbine, should find
518                         // out why, but for now make sure it is noticed.
519                         log.error("", e);
520                         e.printStackTrace();
521                         throw e;
522                     }
523                 }
524             }
525         }
526     }
527 
528     /***
529      * Initialize a manager
530      *
531      * @param name name of the manager
532      * @param className name of the manager class
533      * @throws TorqueException Any exceptions caught during processing will be
534      *         rethrown wrapped into a TorqueException.
535      */
536     private synchronized void initManager(String name, String className)
537             throws TorqueException
538     {
539         AbstractBaseManager manager = (AbstractBaseManager) managers.get(name);
540 
541         if (manager == null)
542         {
543             if (className != null && className.length() != 0)
544             {
545                 try
546                 {
547                     manager = (AbstractBaseManager)
548                             Class.forName(className).newInstance();
549                     managers.put(name, manager);
550                 }
551                 catch (Exception e)
552                 {
553                     throw new TorqueException("Could not instantiate "
554                             + "manager associated with class: "
555                             + name, e);
556                 }
557             }
558         }
559     }
560 
561     /***
562      * Determine whether Torque has already been initialized.
563      *
564      * @return true if Torque is already initialized
565      */
566     public boolean isInit()
567     {
568         return isInit;
569     }
570 
571     /***
572      * Sets the configuration for Torque and all dependencies.
573      * The prefix <code>TORQUE_KEY</code> needs to be removed from the
574      * configuration keys for the provided configuration.
575      *
576      * @param conf the Configuration.
577      */
578     public void setConfiguration(Configuration conf)
579     {
580         log.debug("setConfiguration(" + conf + ")");
581         this.conf = conf;
582     }
583 
584     /***
585      * Get the configuration for this component.
586      *
587      * @return the Configuration
588      */
589     public Configuration getConfiguration()
590     {
591         log.debug("getConfiguration() = " + conf);
592         return conf;
593     }
594 
595     /***
596      * This method returns a Manager for the given name.
597      *
598      * @param name name of the manager
599      * @return a Manager
600      */
601     public AbstractBaseManager getManager(String name)
602     {
603         AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
604         if (m == null)
605         {
606             log.error("No configured manager for key " + name + ".");
607         }
608         return m;
609     }
610 
611     /***
612      * This methods returns either the Manager from the configuration file,
613      * or the default one provided by the generated code.
614      *
615      * @param name name of the manager
616      * @param defaultClassName the class to use if name has not been configured
617      * @return a Manager
618      */
619     public AbstractBaseManager getManager(String name,
620             String defaultClassName)
621     {
622         AbstractBaseManager m = (AbstractBaseManager) managers.get(name);
623         if (m == null)
624         {
625             log.debug("Added late Manager mapping for Class: "
626                     + name + " -> " + defaultClassName);
627 
628             try
629             {
630                 initManager(name, defaultClassName);
631             }
632             catch (TorqueException e)
633             {
634                 log.error(e.getMessage(), e);
635             }
636 
637             // Try again now that the default manager should be in the map
638             m = (AbstractBaseManager) managers.get(name);
639         }
640 
641         return m;
642     }
643 
644     /***
645      * Shuts down the service.
646      *
647      * This method halts the IDBroker's daemon thread in all of
648      * the DatabaseMap's. It also closes all SharedPoolDataSourceFactories
649      * and PerUserPoolDataSourceFactories initialized by Torque.
650      * @exception TorqueException if a DataSourceFactory could not be closed
651      *            cleanly. Only the first exception is rethrown, any following
652      *            exceptions are logged but ignored.
653      */
654     public synchronized void shutdown()
655         throws TorqueException
656     {
657         // stop the idbrokers
658         synchronized (databases)
659         {
660             for (Iterator it = databases.values().iterator(); it.hasNext();)
661             {
662                 Database database = (Database) it.next();
663                 IDBroker idBroker = database.getIDBroker();
664                 if (idBroker != null)
665                 {
666                     idBroker.stop();
667                 }
668             }
669         }
670 
671         // shut down the data source factories
672         TorqueException exception = null;
673         synchronized (databases)
674         {
675             for (Iterator it = databases.keySet().iterator(); it.hasNext();)
676             {
677                 Object databaseKey = it.next();
678 
679                 Database database
680                         = (Database) databases.get(databaseKey);
681                 if (DEFAULT_NAME.equals(databaseKey) && defaultDsfIsReference)
682                 {
683                     // the DataSourceFactory of the database with the name
684                     // DEFAULT_NAME is just a reference to aynother entry.
685                     // Do not close because this leads to closing
686                     // the same DataSourceFactory twice.
687                     database.setDataSourceFactory(null);
688                     break;
689                 }
690 
691                 try
692                 {
693                     DataSourceFactory dataSourceFactory
694                             = database.getDataSourceFactory();
695                     if (dataSourceFactory != null)
696                     {
697                         dataSourceFactory.close();
698                         database.setDataSourceFactory(null);
699                     }
700                 }
701                 catch (TorqueException e)
702                 {
703                     log.error("Error while closing the DataSourceFactory "
704                             + databaseKey,
705                             e);
706                     if (exception == null)
707                     {
708                         exception = e;
709                     }
710                 }
711             }
712         }
713         if (exception != null)
714         {
715             throw exception;
716         }
717         resetConfiguration();
718     }
719 
720     /***
721      * Resets some internal configuration variables to
722      * their defaults.
723      */
724     private void resetConfiguration()
725     {
726         mapBuilders = Collections.synchronizedList(new ArrayList());
727         managers = new HashMap();
728         isInit = false;
729     }
730 
731     /***
732      * Returns the default database map information.
733      *
734      * @return A DatabaseMap.
735      * @throws TorqueException Any exceptions caught during processing will be
736      *         rethrown wrapped into a TorqueException.
737      */
738     public DatabaseMap getDatabaseMap()
739             throws TorqueException
740     {
741         return getDatabaseMap(getDefaultDB());
742     }
743 
744     /***
745      * Returns the database map information. Name relates to the name
746      * of the connection pool to associate with the map.
747      *
748      * @param name The name of the database corresponding to the
749      *        <code>DatabaseMap</code> to retrieve.
750      * @return The named <code>DatabaseMap</code>.
751      * @throws TorqueException Any exceptions caught during processing will be
752      *         rethrown wrapped into a TorqueException.
753      */
754     public DatabaseMap getDatabaseMap(String name)
755             throws TorqueException
756     {
757         if (name == null)
758         {
759             throw new TorqueException ("DatabaseMap name was null!");
760         }
761 
762         Database database = getOrCreateDatabase(name);
763         return database.getDatabaseMap();
764     }
765 
766     /***
767      * Register a MapBuilder
768      *
769      * @param className the MapBuilder
770      */
771     public void registerMapBuilder(String className)
772     {
773         mapBuilders.add(className);
774     }
775 
776     /***
777      * This method returns a Connection from the default pool.
778      *
779      * @return The requested connection, never null.
780      * @throws TorqueException Any exceptions caught during processing will be
781      *         rethrown wrapped into a TorqueException.
782      */
783     public Connection getConnection()
784             throws TorqueException
785     {
786         return getConnection(getDefaultDB());
787     }
788 
789     /***
790      * Returns a database connection to the database with the key
791      * <code>name</code>.
792      * @param name The database name.
793      * @return a database connection, never null.
794      * @throws TorqueException If no DataSourceFactory is configured for the
795      *         named database, the connection information is wrong, or the
796      *         connection cannot be returned for any other reason.
797      */
798     public Connection getConnection(String name)
799             throws TorqueException
800     {
801         try
802         {
803             return getDatabase(name)
804                     .getDataSourceFactory()
805                     .getDataSource()
806                     .getConnection();
807         }
808         catch (SQLException se)
809         {
810             throw new TorqueException(se);
811         }
812     }
813 
814     /***
815      * Returns the DataSourceFactory for the database with the name
816      * <code>name</code>.
817      *
818      * @param name The name of the database to get the DSF for.
819      * @return A DataSourceFactory object, never null.
820      * @throws TorqueException if Torque is not initiliaized, or
821      *         no DatasourceFactory is configured for the given name.
822      */
823     public DataSourceFactory getDataSourceFactory(String name)
824             throws TorqueException
825     {
826         Database database = getDatabase(name);
827 
828         DataSourceFactory dsf = null;
829         if (database != null)
830         {
831             dsf = database.getDataSourceFactory();
832         }
833 
834         if (dsf == null)
835         {
836             throw new TorqueException(
837                     "There was no DataSourceFactory "
838                     + "configured for the connection " + name);
839         }
840 
841         return dsf;
842     }
843 
844     /***
845      * This method returns a Connection using the given parameters.
846      * You should only use this method if you need user based access to the
847      * database!
848      *
849      * @param name The database name.
850      * @param username The name of the database user.
851      * @param password The password of the database user.
852      * @return A Connection.
853      * @throws TorqueException Any exceptions caught during processing will be
854      *         rethrown wrapped into a TorqueException.
855      */
856     public Connection getConnection(String name, String username,
857             String password)
858             throws TorqueException
859     {
860         try
861         {
862             return getDataSourceFactory(name)
863                     .getDataSource().getConnection(username, password);
864         }
865         catch (SQLException se)
866         {
867             throw new TorqueException(se);
868         }
869     }
870 
871     /***
872      * Returns the database adapter for a specific database.
873      *
874      * @param name the name of the database to get the adapter for.
875      * @return The corresponding database adapter, or null if no database
876      *         adapter is defined for the given database.
877      * @throws TorqueException Any exceptions caught during processing will be
878      *         rethrown wrapped into a TorqueException.
879      */
880     public DB getDB(String name) throws TorqueException
881     {
882         Database database = getDatabase(name);
883         if (database == null)
884         {
885             return null;
886         }
887         return database.getAdapter();
888     }
889 
890     ///////////////////////////////////////////////////////////////////////////
891 
892     /***
893      * Returns the name of the default database.
894      *
895      * @return name of the default DB, or null if Torque is not initialized yet
896      */
897     public String getDefaultDB()
898     {
899         return defaultDBName;
900     }
901 
902     /***
903      * Closes a connection.
904      *
905      * @param con A Connection to close.
906      */
907     public void closeConnection(Connection con)
908     {
909         if (con != null)
910         {
911             try
912             {
913                 con.close();
914             }
915             catch (SQLException e)
916             {
917                 log.error("Error occured while closing connection.", e);
918             }
919         }
920     }
921 
922     /***
923      * Sets the current schema for a database connection
924      *
925      * @param name The database name.
926      * @param schema The current schema name.
927      * @throws TorqueException Any exceptions caught during processing will be
928      *         rethrown wrapped into a TorqueException.
929      */
930     public void setSchema(String name, String schema)
931             throws TorqueException
932     {
933         getOrCreateDatabase(name).setSchema(schema);
934     }
935 
936     /***
937      * This method returns the current schema for a database connection
938      *
939      * @param name The database name.
940      * @return The current schema name. Null means, no schema has been set.
941      * @throws TorqueException Any exceptions caught during processing will be
942      *         rethrown wrapped into a TorqueException.
943      */
944     public String getSchema(String name)
945         throws TorqueException
946     {
947         Database database = getDatabase(name);
948         if (database == null)
949         {
950             return null;
951         }
952         return database.getSchema();
953     }
954 
955     /***
956      * Returns the database for the key <code>databaseName</code>.
957      *
958      * @param databaseName the key to get the database for.
959      * @return the database for the specified key, or null if the database
960      *         does not exist.
961      * @throws TorqueException if Torque is not yet initialized.
962      */
963     public Database getDatabase(String databaseName) throws TorqueException
964     {
965         if (!isInit())
966         {
967             throw new TorqueException("Torque is not initialized.");
968         }
969         return (Database) databases.get(databaseName);
970     }
971 
972     /***
973      * Returns a Map containing all Databases registered to Torque.
974      * The key of the Map is the name of the database, and the value is the
975      * database instance. <br/>
976      * Note that in the very special case where a new database which
977      * is not configured in Torque's configuration gets known to Torque
978      * at a later time, the returned map may change, and there is no way to
979      * protect you against this.
980      *
981      * @return a Map containing all Databases known to Torque, never null.
982      * @throws TorqueException if Torque is not yet initialized.
983      */
984     public Map getDatabases() throws TorqueException
985     {
986         if (!isInit())
987         {
988             throw new TorqueException("Torque is not initialized.");
989         }
990         return Collections.unmodifiableMap(databases);
991     }
992 
993     /***
994      * Returns the database for the key <code>databaseName</code>.
995      * If no database is associated to the specified key,
996      * a new database is created, mapped to the specified key, and returned.
997      *
998      * @param databaseName the key to get the database for.
999      * @return the database associated with specified key, or the newly created
1000      *         database, never null.
1001      */
1002     public Database getOrCreateDatabase(String databaseName)
1003     {
1004         synchronized (databases)
1005         {
1006             Database result = (Database) databases.get(databaseName);
1007             if (result == null)
1008             {
1009                 result = new Database(databaseName);
1010                 databases.put(databaseName, result);
1011             }
1012             return result;
1013         }
1014     }
1015 }