/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.service.jdbc.connections.internal; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import org.jboss.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.cfg.AvailableSettings; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.MultiTenancyStrategy; import org.hibernate.cfg.Environment; import org.hibernate.internal.util.beans.BeanInfoHelper; import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.hibernate.service.spi.BasicServiceInitiator; /** * Instantiates and configures an appropriate {@link ConnectionProvider}. * * @author Gavin King * @author Steve Ebersole */ public class ConnectionProviderInitiator implements BasicServiceInitiator<ConnectionProvider> { public static final ConnectionProviderInitiator INSTANCE = new ConnectionProviderInitiator(); private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, ConnectionProviderInitiator.class.getName()); public static final String C3P0_PROVIDER_CLASS_NAME = "org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider"; public static final String PROXOOL_PROVIDER_CLASS_NAME = "org.hibernate.service.jdbc.connections.internal.ProxoolConnectionProvider"; public static final String INJECTION_DATA = "hibernate.connection_provider.injection_data"; // mapping from legacy connection provider name to actual // connection provider that will be used private static final Map<String,String> LEGACY_CONNECTION_PROVIDER_MAPPING; static { LEGACY_CONNECTION_PROVIDER_MAPPING = new HashMap<String,String>( 5 ); LEGACY_CONNECTION_PROVIDER_MAPPING.put( "org.hibernate.connection.DatasourceConnectionProvider", DatasourceConnectionProviderImpl.class.getName() ); LEGACY_CONNECTION_PROVIDER_MAPPING.put( "org.hibernate.connection.DriverManagerConnectionProvider", DriverManagerConnectionProviderImpl.class.getName() ); LEGACY_CONNECTION_PROVIDER_MAPPING.put( "org.hibernate.connection.UserSuppliedConnectionProvider", UserSuppliedConnectionProviderImpl.class.getName() ); LEGACY_CONNECTION_PROVIDER_MAPPING.put( "org.hibernate.connection.C3P0ConnectionProvider", C3P0_PROVIDER_CLASS_NAME ); LEGACY_CONNECTION_PROVIDER_MAPPING.put( "org.hibernate.connection.ProxoolConnectionProvider", PROXOOL_PROVIDER_CLASS_NAME ); } @Override public Class<ConnectionProvider> getServiceInitiated() { return ConnectionProvider.class; } @Override public ConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) { if ( MultiTenancyStrategy.determineMultiTenancyStrategy( configurationValues ) != MultiTenancyStrategy.NONE ) { // nothing to do, but given the separate hierarchies have to handle this here. } final ClassLoaderService classLoaderService = registry.getService( ClassLoaderService.class ); ConnectionProvider connectionProvider = null; String providerClassName = getConfiguredConnectionProviderName( configurationValues ); if ( providerClassName != null ) { connectionProvider = instantiateExplicitConnectionProvider( providerClassName, classLoaderService ); } else if ( configurationValues.get( Environment.DATASOURCE ) != null ) { connectionProvider = new DatasourceConnectionProviderImpl(); } if ( connectionProvider == null ) { if ( c3p0ConfigDefined( configurationValues ) && c3p0ProviderPresent( classLoaderService ) ) { connectionProvider = instantiateExplicitConnectionProvider( C3P0_PROVIDER_CLASS_NAME, classLoaderService ); } } if ( connectionProvider == null ) { if ( proxoolConfigDefined( configurationValues ) && proxoolProviderPresent( classLoaderService ) ) { connectionProvider = instantiateExplicitConnectionProvider( PROXOOL_PROVIDER_CLASS_NAME, classLoaderService ); } } if ( connectionProvider == null ) { if ( configurationValues.get( Environment.URL ) != null ) { connectionProvider = new DriverManagerConnectionProviderImpl(); } } if ( connectionProvider == null ) { LOG.noAppropriateConnectionProvider(); connectionProvider = new UserSuppliedConnectionProviderImpl(); } final Map injectionData = (Map) configurationValues.get( INJECTION_DATA ); if ( injectionData != null && injectionData.size() > 0 ) { final ConnectionProvider theConnectionProvider = connectionProvider; new BeanInfoHelper( connectionProvider.getClass() ).applyToBeanInfo( connectionProvider, new BeanInfoHelper.BeanInfoDelegate() { public void processBeanInfo(BeanInfo beanInfo) throws Exception { PropertyDescriptor[] descritors = beanInfo.getPropertyDescriptors(); for ( int i = 0, size = descritors.length; i < size; i++ ) { String propertyName = descritors[i].getName(); if ( injectionData.containsKey( propertyName ) ) { Method method = descritors[i].getWriteMethod(); method.invoke( theConnectionProvider, injectionData.get( propertyName ) ); } } } } ); } return connectionProvider; } private String getConfiguredConnectionProviderName( Map configurationValues ) { String providerClassName = ( String ) configurationValues.get( Environment.CONNECTION_PROVIDER ); if ( LEGACY_CONNECTION_PROVIDER_MAPPING.containsKey( providerClassName ) ) { String actualProviderClassName = LEGACY_CONNECTION_PROVIDER_MAPPING.get( providerClassName ); LOG.providerClassDeprecated(providerClassName, actualProviderClassName); providerClassName = actualProviderClassName; } return providerClassName; } private ConnectionProvider instantiateExplicitConnectionProvider( String providerClassName, ClassLoaderService classLoaderService) { try { LOG.instantiatingExplicitConnectionProvider( providerClassName ); return (ConnectionProvider) classLoaderService.classForName( providerClassName ).newInstance(); } catch ( Exception e ) { throw new HibernateException( "Could not instantiate connection provider [" + providerClassName + "]", e ); } } private boolean c3p0ProviderPresent(ClassLoaderService classLoaderService) { try { classLoaderService.classForName( C3P0_PROVIDER_CLASS_NAME ); } catch ( Exception e ) { LOG.c3p0ProviderClassNotFound(C3P0_PROVIDER_CLASS_NAME); return false; } return true; } private static boolean c3p0ConfigDefined(Map configValues) { for ( Object key : configValues.keySet() ) { if ( String.class.isInstance( key ) && ( (String) key ).startsWith( AvailableSettings.C3P0_CONFIG_PREFIX ) ) { return true; } } return false; } private boolean proxoolProviderPresent(ClassLoaderService classLoaderService) { try { classLoaderService.classForName( PROXOOL_PROVIDER_CLASS_NAME ); } catch ( Exception e ) { LOG.proxoolProviderClassNotFound(PROXOOL_PROVIDER_CLASS_NAME); return false; } return true; } private static boolean proxoolConfigDefined(Map configValues) { for ( Object key : configValues.keySet() ) { if ( String.class.isInstance( key ) && ( (String) key ).startsWith( AvailableSettings.PROXOOL_CONFIG_PREFIX ) ) { return true; } } return false; } /** * Build the connection properties capable of being passed to the {@link java.sql.DriverManager#getConnection} * forms taking {@link Properties} argument. We seek out all keys in the passed map which start with * {@code hibernate.connection.}, using them to create a new {@link Properties} instance. The keys in this * new {@link Properties} have the {@code hibernate.connection.} prefix trimmed. * * @param properties The map from which to build the connection specific properties. * * @return The connection properties. */ public static Properties getConnectionProperties(Map<?,?> properties) { Properties result = new Properties(); for ( Map.Entry entry : properties.entrySet() ) { if ( ! ( String.class.isInstance( entry.getKey() ) ) || ! String.class.isInstance( entry.getValue() ) ) { continue; } final String key = (String) entry.getKey(); final String value = (String) entry.getValue(); if ( key.startsWith( Environment.CONNECTION_PREFIX ) ) { if ( SPECIAL_PROPERTIES.contains( key ) ) { if ( Environment.USER.equals( key ) ) { result.setProperty( "user", value ); } } else { result.setProperty( key.substring( Environment.CONNECTION_PREFIX.length() + 1 ), value ); } } } return result; } private static final Set<String> SPECIAL_PROPERTIES; static { SPECIAL_PROPERTIES = new HashSet<String>(); SPECIAL_PROPERTIES.add( Environment.DATASOURCE ); SPECIAL_PROPERTIES.add( Environment.URL ); SPECIAL_PROPERTIES.add( Environment.CONNECTION_PROVIDER ); SPECIAL_PROPERTIES.add( Environment.POOL_SIZE ); SPECIAL_PROPERTIES.add( Environment.ISOLATION ); SPECIAL_PROPERTIES.add( Environment.DRIVER ); SPECIAL_PROPERTIES.add( Environment.USER ); } }