UcpDataSourceConfig.java

package com.maybeitssquid.rotatingsecrets.ucp;

import java.sql.SQLException;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Spring configuration for Oracle Universal Connection Pool (UCP) DataSource.
 *
 * <p>This configuration creates an Oracle UCP {@link PoolDataSource} bean with configurable
 * connection pool settings. Oracle UCP is designed specifically for Oracle databases and provides
 * advanced features not available in generic connection pools.
 *
 * <h2>Oracle UCP Advantages</h2>
 *
 * <ul>
 *   <li><b>Fast Application Notification (FAN)</b> - Automatic failover on RAC/Data Guard events
 *   <li><b>Oracle Wallet Integration</b> - Enterprise credential management
 *   <li><b>Transparent Application Continuity</b> - Automatic request replay on failover
 *   <li><b>Service-Aware Connections</b> - Different pools for different database services
 * </ul>
 *
 * <h2>Configuration Properties</h2>
 *
 * <p>All properties are prefixed with {@code spring.datasource.ucp.}:
 *
 * <ul>
 *   <li>{@code url} - JDBC URL for the Oracle database (required)
 *   <li>{@code connection-factory-class-name} - JDBC driver class (required)
 *   <li>{@code user} - Initial database username (required)
 *   <li>{@code password} - Initial database password (required)
 *   <li>{@code pool-name} - Connection pool name (default: UCPPool)
 *   <li>{@code initial-pool-size} - Initial connections to create (default: 2)
 *   <li>{@code min-pool-size} - Minimum pool size (default: 2)
 *   <li>{@code max-pool-size} - Maximum pool size (default: 10)
 *   <li>{@code connection-wait-timeout} - Seconds to wait for a connection (default: 20)
 *   <li>{@code inactive-connection-timeout} - Seconds before idle connection is closed (default:
 *       30)
 *   <li>{@code max-connection-reuse-time} - Maximum seconds to reuse a connection (default: 1800)
 * </ul>
 *
 * @see PoolDataSource
 * @see UcpCredentialsUpdater
 */
@Configuration
public class UcpDataSourceConfig {

  /** Default constructor. */
  public UcpDataSourceConfig() {
    // Configuration class for Oracle UCP DataSource
  }

  /** JDBC URL for the Oracle database connection. */
  @Value("${spring.datasource.ucp.url}")
  private String url;

  /** Fully qualified class name of the JDBC connection factory (driver). */
  @Value("${spring.datasource.ucp.connection-factory-class-name}")
  private String connectionFactoryClassName;

  /** Initial database username for pool connections. */
  @Value("${spring.datasource.ucp.user}")
  private String user;

  /** Initial database password for pool connections. */
  @Value("${spring.datasource.ucp.password}")
  private String password;

  /** Name of the connection pool for identification and management. */
  @Value("${spring.datasource.ucp.pool-name:UCPPool}")
  private String poolName;

  /** Number of connections to create when the pool is initialized. */
  @Value("${spring.datasource.ucp.initial-pool-size:2}")
  private int initialPoolSize;

  /** Minimum number of connections the pool should maintain. */
  @Value("${spring.datasource.ucp.min-pool-size:2}")
  private int minPoolSize;

  /** Maximum number of connections the pool can create. */
  @Value("${spring.datasource.ucp.max-pool-size:10}")
  private int maxPoolSize;

  /** Seconds to wait when requesting a connection before timing out. */
  @Value("${spring.datasource.ucp.connection-wait-timeout:20}")
  private int connectionWaitTimeout;

  /** Seconds an idle connection can remain in the pool before being closed. */
  @Value("${spring.datasource.ucp.inactive-connection-timeout:30}")
  private int inactiveConnectionTimeout;

  /** Maximum seconds a connection can be reused before being recycled. */
  @Value("${spring.datasource.ucp.max-connection-reuse-time:1800}")
  private int maxConnectionReuseTime;

  /**
   * Creates and configures the Oracle UCP PoolDataSource bean.
   *
   * <p>The pool is configured with all connection and timeout settings from application properties.
   * The returned {@link PoolDataSource} can be used directly or wrapped for additional
   * functionality.
   *
   * @return a configured Oracle UCP PoolDataSource
   * @throws SQLException if the pool cannot be configured or initialized
   */
  @Bean
  public PoolDataSource poolDataSource() throws SQLException {
    PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
    pds.setConnectionPoolName(poolName);
    pds.setConnectionFactoryClassName(connectionFactoryClassName);
    pds.setURL(url);
    pds.setUser(user);
    pds.setPassword(password);
    pds.setInitialPoolSize(initialPoolSize);
    pds.setMinPoolSize(minPoolSize);
    pds.setMaxPoolSize(maxPoolSize);
    pds.setConnectionWaitTimeout(connectionWaitTimeout);
    pds.setInactiveConnectionTimeout(inactiveConnectionTimeout);
    pds.setMaxConnectionReuseTime(maxConnectionReuseTime);
    return pds;
  }

  /**
   * Creates the UCP credentials updater bean for credential rotation support.
   *
   * <p>The updater is registered with the {@link
   * com.maybeitssquid.rotatingsecrets.CredentialsProviderService} to receive notifications when
   * database credentials are rotated. It handles updating the pool's credentials and refreshing
   * connections.
   *
   * @param poolDataSource the Oracle UCP PoolDataSource to manage
   * @return a credentials updater configured for the given pool
   * @see UcpCredentialsUpdater
   */
  @Bean("ucpUpdater")
  public UcpCredentialsUpdater ucpCredentialsUpdater(PoolDataSource poolDataSource) {
    return new UcpCredentialsUpdater(poolDataSource);
  }
}