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