/* * Copyright 2005 Ralf Joachim * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.castor.jdo.engine; import java.util.Hashtable; import javax.sql.DataSource; import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.core.util.AbstractProperties; import org.castor.core.util.Messages; import org.castor.cpa.CPAProperties; import org.castor.cpa.persistence.sql.connection.ConnectionFactory; import org.castor.cpa.persistence.sql.connection.DataSourceConnectionFactory; import org.castor.cpa.persistence.sql.connection.DriverConnectionFactory; import org.castor.cpa.persistence.sql.connection.JNDIConnectionFactory; import org.castor.cpa.util.JDOClassDescriptorResolver; import org.castor.jdo.conf.Database; import org.castor.jdo.conf.DatabaseChoice; import org.castor.jdo.conf.JdoConf; import org.castor.jdo.util.JDOConfFactory; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; /** * @author <a href="arkin@intalio.com">Assaf Arkin</a> * @author <a href="mailto:ferret AT frii dot com">Bruce Snyder</a> * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a> * @version $Revision$ $Date: 2006-04-10 16:39:24 -0600 (Mon, 10 Apr 2006) $ * @since 0.9.9 */ public final class DatabaseRegistry { /** The <a href="http://jakarta.apache.org/commons/logging/">Jakarta * Commons Logging</a> instance used for all logging. */ private static final Log LOG = LogFactory.getLog(DatabaseRegistry.class); /** Map of all registered connection factories by name. */ private static final Hashtable CONTEXTS = new Hashtable(); /** * Instantiates a DataSourceConnectionFactory with given name, engine, datasource * and mapping. * * @param name The Name of the database configuration. * @param engine The Name of the persistence factory to use. * @param datasource The preconfigured datasource to use for creating connections. * @param mapping The previously loaded mapping. * @param txManager The transaction manager to use. * @throws MappingException If LockEngine could not be initialized. */ public static synchronized void loadDatabase( final String name, final String engine, final DataSource datasource, final Mapping mapping, final TransactionManager txManager) throws MappingException { boolean useProxies = CPAProperties.getInstance().getBoolean( CPAProperties.USE_JDBC_PROXIES, true); ConnectionFactory cf = new DataSourceConnectionFactory(datasource, useProxies); DatabaseContext context = new DatabaseContext(name, engine, mapping, txManager, cf); if (CONTEXTS.put(name, context) != null) { LOG.warn(Messages.format("jdo.configLoadedTwice", name)); } } /** * Instantiates a ConnectionFactory from the JDO configuration file. * * @param source {@link InputSource} pointing to the JDO configuration. * @param resolver An entity resolver. * @param loader A class loader * @throws MappingException If the database cannot be instantiated/loadeed. */ public static synchronized void loadDatabase(final InputSource source, final EntityResolver resolver, final ClassLoader loader) throws MappingException { loadDatabase(source, resolver, loader, null); } /** * Instantiates a ConnectionFactory from the JDO configuration file. * * @param source * {@link InputSource} pointing to the JDO configuration. * @param resolver * An entity resolver. * @param loader * A class loader * @param classDescriptorResolver * {@link ClassDescriptorResolver} used for class to class * descriptor resolution. * @throws MappingException * If the database cannot be instantiated/loaded. */ public static synchronized void loadDatabase(final InputSource source, final EntityResolver resolver, final ClassLoader loader, final JDOClassDescriptorResolver classDescriptorResolver) throws MappingException { // Load the JDO configuration file from the specified input source. JdoConf jdoConf = null; jdoConf = JDOConfFactory.createJdoConf(source, resolver, loader); LOG.debug("Loaded jdo conf successfully"); loadDatabase(jdoConf, resolver, loader, source.getSystemId(), classDescriptorResolver); } /** * Creates a entry for every database and associates them with their name in a * map. It then instantiates all databases if * 'org.exolab.castor.jdo.DatabaseInitializeAtLoad' key can not be found or is * set to <code>true</code> in castor.properties file. If above property is set * to <code>false</code> it will instantiate all databases only when they are * needed. * * @param jdoConf An in-memory jdo configuration. * @param resolver An entity resolver. * @param loader A class loader * @param baseURI The base URL for the mapping * @throws MappingException If the database cannot be instantiated/loadeed. */ public static synchronized void loadDatabase(final JdoConf jdoConf, final EntityResolver resolver, final ClassLoader loader, final String baseURI) throws MappingException { loadDatabase(jdoConf, resolver, loader, baseURI, null); } /** * Creates a entry for every database and associates them with their name in * a map. It then instantiates all databases if * 'org.exolab.castor.jdo.DatabaseInitializeAtLoad' key can not be found or * is set to <code>true</code> in castor.properties file. If above * property is set to <code>false</code> it will instantiate all databases * only when they are needed. * * @param jdoConf * An in-memory jdo configuration. * @param resolver * An entity resolver. * @param loader * A class loader * @param baseURI * The base URL for the mapping * @param classDescriptorResolver * {@link ClassDescriptorResolver} used for class to class * descriptor resolution. * @throws MappingException * If the database cannot be instantiated/loadeed. */ public static synchronized void loadDatabase(final JdoConf jdoConf, final EntityResolver resolver, final ClassLoader loader, final String baseURI, final JDOClassDescriptorResolver classDescriptorResolver) throws MappingException { // Do we need to initialize database now or should we // wait until we want to use it. AbstractProperties properties = CPAProperties.getInstance(); boolean init = properties.getBoolean(CPAProperties.INITIALIZE_AT_LOAD, true); // Load the JDO configuration file from the specified input source. // databases = JDOConfLoader.getDatabases(baseURI, resolver); Database[] databases = jdoConf.getDatabase(); DatabaseContext context; for (int i = 0; i < databases.length; i++) { // Load the mapping file from the URL specified in the database // configuration file, relative to the configuration file. // Fail if cannot load the mapping for whatever reason. Mapping mapping = new Mapping(loader); if (resolver != null) { mapping.setEntityResolver(resolver); } if (baseURI != null) { mapping.setBaseURL(baseURI); } context = DatabaseRegistry.createDatabaseContext(jdoConf, i, mapping); context.setClassDescriptorResolver(classDescriptorResolver); if (init) { context.initialize(); } String name = databases[i].getName(); if (CONTEXTS.put(name, context) != null) { LOG.warn(Messages.format("jdo.configLoadedTwice", name)); } } } /** * Factory methode to create a ConnectionFactory for given database configuration * and given mapping. * * @param jdoConf An in-memory jdo configuration. * @param index Index of the database configuration inside the jdo configuration. * @param mapping The mapping to load. * @return The ConnectionFactory. * @throws MappingException If the database cannot be instantiated/loadeed. */ private static DatabaseContext createDatabaseContext( final JdoConf jdoConf, final int index, final Mapping mapping) throws MappingException { boolean useProxies = CPAProperties.getInstance().getBoolean( CPAProperties.USE_JDBC_PROXIES, true); ConnectionFactory factory; DatabaseChoice choice = jdoConf.getDatabase(index).getDatabaseChoice(); if (choice == null) { String name = jdoConf.getDatabase(index).getName(); String msg = Messages.format("jdo.missingDataSource", name); LOG.error(msg); throw new MappingException(msg); } if (choice.getDriver() != null) { // JDO configuration file specifies a driver, use the driver // properties to create a new registry object. factory = new DriverConnectionFactory(choice.getDriver(), useProxies); } else if (choice.getDataSource() != null) { // JDO configuration file specifies a DataSource object, use the // DataSource which was configured from the JDO configuration file // to create a new registry object. ClassLoader loader = mapping.getClassLoader(); factory = new DataSourceConnectionFactory(choice.getDataSource(), useProxies, loader); } else if (choice.getJndi() != null) { // JDO configuration file specifies a DataSource lookup through JNDI, // locate the DataSource object frome the JNDI namespace and use it. factory = new JNDIConnectionFactory(choice.getJndi(), useProxies); } else { String name = jdoConf.getDatabase(index).getName(); String msg = Messages.format("jdo.missingDataSource", name); LOG.error(msg); throw new MappingException(msg); } return new DatabaseContext(jdoConf, index, mapping, factory); } /** * Check if any database configuration has been loaded. * * @return <code>true</code> if a databases configuration has been loaded. */ public static boolean hasDatabaseRegistries() { return (!CONTEXTS.isEmpty()); } /** * Check if database configuration with given name has been loaded. * * @param name Name of the database to check if loaded. * @return <code>true</code> if databases configuration has been loaded. */ public static boolean isDatabaseRegistred(final String name) { return CONTEXTS.containsKey(name); } /** * Get the ConnectionFactory for the given database name. * * @param name Name of the database configuration. * @return The ConnectionFactory for the given database name. * @throws MappingException If database can not be instantiated. */ public static DatabaseContext getDatabaseContext(final String name) throws MappingException { if (LOG.isDebugEnabled()) { LOG.debug("Fetching ConnectionFactory: " + name); } DatabaseContext context = (DatabaseContext) CONTEXTS.get(name); if (context == null) { String msg = Messages.format("jdo.missingDataSource", name); LOG.error(msg); throw new MappingException(msg); } context.initialize(); return context; } /** * Reset all database configurations. */ public static void clear() { CONTEXTS.clear(); } /** * Unload the database configuration with given name. * * @param name Name of the database to be unloaded. */ public static void unloadDatabase(final String name) { CONTEXTS.remove(name); } /** * Hide constructor of utility class. */ private DatabaseRegistry() { } }