io.agroal.pool.ConnectionFactory Maven / Gradle / Ivy
// Copyright (C) 2017 Red Hat, Inc. and individual contributors as indicated by the @author tags.
// You may not use this file except in compliance with the Apache License, Version 2.0.
package io.agroal.pool;
import io.agroal.api.AgroalDataSourceListener;
import io.agroal.api.configuration.AgroalConnectionFactoryConfiguration;
import io.agroal.api.security.AgroalSecurityProvider;
import io.agroal.api.transaction.TransactionIntegration.ResourceRecoveryFactory;
import io.agroal.pool.util.PropertyInjector;
import io.agroal.pool.util.XAConnectionAdaptor;
import javax.sql.XAConnection;
import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Locale;
import java.util.Properties;
import static io.agroal.pool.util.ListenerHelper.fireOnWarning;
/**
* @author Luis Barreiro
*/
public final class ConnectionFactory implements ResourceRecoveryFactory {
private static final String URL_PROPERTY_NAME = "url";
private static final Properties EMPTY_PROPERTIES = new Properties();
private final AgroalConnectionFactoryConfiguration configuration;
private final AgroalDataSourceListener[] listeners;
private final Properties jdbcProperties;
private final Mode factoryMode;
// these are the sources for connections, that will be used depending on the mode
private java.sql.Driver driver;
private javax.sql.DataSource dataSource;
private javax.sql.XADataSource xaDataSource;
private javax.sql.XADataSource xaRecoveryDataSource;
private PropertyInjector injector;
public ConnectionFactory(AgroalConnectionFactoryConfiguration configuration, AgroalDataSourceListener... listeners) {
this.configuration = configuration;
this.listeners = listeners;
this.jdbcProperties = new Properties();
configuration.jdbcProperties().forEach( jdbcProperties::put );
this.factoryMode = Mode.fromClass( configuration.connectionProviderClass() );
switch ( factoryMode ) {
case XA_DATASOURCE:
this.injector = new PropertyInjector( configuration.connectionProviderClass() );
this.xaDataSource = newXADataSource( jdbcProperties );
this.xaRecoveryDataSource = newXADataSource( jdbcProperties );
break;
case DATASOURCE:
this.injector = new PropertyInjector( configuration.connectionProviderClass() );
this.dataSource = newDataSource( jdbcProperties );
break;
case DRIVER:
this.driver = newDriver();
break;
}
}
private javax.sql.XADataSource newXADataSource(Properties properties) {
javax.sql.XADataSource newDataSource;
try {
newDataSource = configuration.connectionProviderClass().asSubclass( javax.sql.XADataSource.class ).getDeclaredConstructor().newInstance();
} catch ( IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e ) {
throw new RuntimeException( "Unable to instantiate javax.sql.XADataSource", e );
}
if ( configuration.jdbcUrl() != null && !configuration.jdbcUrl().isEmpty() ) {
injectUrlProperty( newDataSource, URL_PROPERTY_NAME, configuration.jdbcUrl() );
}
injectJdbcProperties( newDataSource, properties );
return newDataSource;
}
private javax.sql.DataSource newDataSource(Properties properties) {
javax.sql.DataSource newDataSource;
try {
newDataSource = configuration.connectionProviderClass().asSubclass( javax.sql.DataSource.class ).getDeclaredConstructor().newInstance();
} catch ( IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e ) {
throw new RuntimeException( "Unable to instantiate javax.sql.DataSource", e );
}
if ( configuration.jdbcUrl() != null && !configuration.jdbcUrl().isEmpty() ) {
injectUrlProperty( newDataSource, URL_PROPERTY_NAME, configuration.jdbcUrl() );
}
injectJdbcProperties( newDataSource, properties );
return newDataSource;
}
private java.sql.Driver newDriver() {
if ( configuration.connectionProviderClass() == null ) {
try {
return driver = java.sql.DriverManager.getDriver( configuration.jdbcUrl() );
} catch ( SQLException sql ) {
throw new RuntimeException( "Unable to get java.sql.Driver from DriverManager", sql );
}
} else {
try {
driver = configuration.connectionProviderClass().asSubclass( java.sql.Driver.class ).getDeclaredConstructor().newInstance();
if ( !driver.acceptsURL( configuration.jdbcUrl() ) ) {
fireOnWarning( listeners, "Driver does not support the provided URL: " + configuration.jdbcUrl() );
}
return driver;
} catch ( IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e ) {
throw new RuntimeException( "Unable to instantiate java.sql.Driver", e );
} catch ( SQLException e ) {
throw new RuntimeException( "Unable to verify that the java.sql.Driver supports the provided URL", e );
}
}
}
private void injectUrlProperty(Object target, String propertyName, String propertyValue) {
try {
injector.inject( target, propertyName, propertyValue );
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
// AG-134 - Some drivers have setURL() instead of setUrl(), so we retry with upper case.
if ( propertyName.chars().anyMatch( Character::isLowerCase ) ) {
injectUrlProperty( target, propertyName.toUpperCase( Locale.ROOT ), propertyValue );
} else {
fireOnWarning( listeners, "Ignoring property '" + propertyName + "': " + e.getMessage() );
}
}
}
private void injectJdbcProperties(Object target, Properties properties) {
boolean ignoring = false;
for ( String propertyName : properties.stringPropertyNames() ) {
try {
injector.inject( target, propertyName, properties.getProperty( propertyName ) );
} catch ( IllegalArgumentException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e ) {
fireOnWarning( listeners, "Ignoring property '" + propertyName + "': " + e.getMessage() );
ignoring = true;
}
}
if ( ignoring ) {
fireOnWarning( listeners, "Available properties " + Arrays.toString( injector.availableProperties().toArray() ) );
}
}
// --- //
private Properties jdbcProperties() {
Properties properties = new Properties();
properties.putAll( jdbcProperties );
properties.putAll( securityProperties( configuration.principal(), configuration.credentials() ) );
return properties;
}
private Properties recoveryProperties() {
Properties properties = new Properties();
// use the main credentials when recovery credentials are not provided
if ( configuration.recoveryPrincipal() == null && ( configuration.recoveryCredentials() == null || configuration.recoveryCredentials().isEmpty() ) ) {
properties.putAll( securityProperties( configuration.principal(), configuration.credentials() ) );
} else {
properties.putAll( securityProperties( configuration.recoveryPrincipal(), configuration.recoveryCredentials() ) );
}
return properties;
}
private Properties securityProperties(Principal principal, Iterable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy