You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit测试中Oracle UCP连接池启动失败,生产环境及主方法正常

Oracle UCP连接池JUnit测试启动失败问题解决

问题现象

初始化Oracle UCP连接池时,JUnit集成测试始终失败,但生产环境或单独main方法中使用相同配置启动连接池完全正常,所有场景连接属性一致。失败固定发生在this.universalConnectionPool.start();代码处。

报错栈信息

Caused by: com.xelog.nx.wms.hardware.hrl.HRLDriverException: java.lang.AssertionError: pool start procedure finished with wrong life cycle state, lifecycle=Starting
    at com.xelog.nx.wms.hardware.hrl.OracleConnectionProvider.<init>(OracleConnectionProvider.java:128)
    ... 72 more
Caused by: java.lang.AssertionError: pool start procedure finished with wrong life cycle state, lifecycle=Starting
    at oracle.ucp.common.UniversalConnectionPoolBase.start(UniversalConnectionPoolBase.java:903)
    at oracle.ucp.jdbc.oracle.OracleJDBCConnectionPool.start(OracleJDBCConnectionPool.java:143)
    at oracle.ucp.jdbc.oracle.OracleReplayableConnectionConnectionPool.start(OracleReplayableConnectionConnectionPool.java:102)
    at oracle.ucp.UniversalConnectionPool.start(UniversalConnectionPool.java:224)
    ... 74 more

连接池实现代码

package com.test;

import oracle.ucp.UniversalConnectionPool;
import oracle.ucp.UniversalConnectionPoolAdapter;
import oracle.ucp.UniversalConnectionPoolException;
import oracle.ucp.UniversalConnectionPoolLifeCycleState;
import oracle.ucp.admin.UniversalConnectionPoolManager;
import oracle.ucp.admin.UniversalConnectionPoolManagerImpl;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

public class OracleConnectionProvider {
    
    private static final Logger LOG = LogManager.getLogger(OracleConnectionProvider.class);
    
    private static final long MAX_STARTUP_WAIT_MILLIS = 5000;//30000;
    
    private final String connectionFactory;
    private final String url;
    private final String user;
    private final String password;
    
    private final String connectionPoolName;
    private PoolDataSource pds;
    private UniversalConnectionPool universalConnectionPool;
    
    public OracleConnectionProvider(final String connectionFactory, final String url, final String user, final String password,
                                    final Map<String, String> propertyMap, final String poolName, final int minPoolSize, final int maxPoolSize) throws HRLDriverException {
        
        this.connectionFactory = connectionFactory;
        this.url = url;
        this.user = user;
        this.password = password;
        
        this.connectionPoolName = poolName;
        
        final long startOracleConnectionProvider = System.currentTimeMillis();
        try {
            final UniversalConnectionPoolManager ucpManager = UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager();
            
            this.pds = PoolDataSourceFactory.getPoolDataSource();
            this.pds.setConnectionPoolName(this.connectionPoolName);
            this.pds.setConnectionFactoryClassName(this.connectionFactory);
            this.pds.setURL(this.url);
            this.pds.setUser(this.user);
            this.pds.setPassword(this.password);
            this.pds.setValidateConnectionOnBorrow(true);
            this.pds.setInitialPoolSize(minPoolSize);
            this.pds.setMinPoolSize(minPoolSize);
            this.pds.setMaxPoolSize(maxPoolSize);

            // Default is 30secs. Set the frequency in seconds to enforce the timeout
            // properties. Applies to inactiveConnectionTimeout(int secs),
            // AbandonedConnectionTimeout(secs)& TimeToLiveConnectionTimeout(int secs).
            // Range of valid values is 0 to Integer.MAX_VALUE. .
            this.pds.setTimeoutCheckInterval(5);
            
            // Default is 0. Set the maximum time, in seconds, that a
            // connection remains available in the connection pool.
            this.pds.setInactiveConnectionTimeout(10);
            
            if (propertyMap != null && !propertyMap.isEmpty()) {
                final Properties properties = this.pds.getConnectionProperties();
                properties.putAll(propertyMap);
                this.pds.setConnectionProperties(properties);
            } else {
                this.pds.setConnectionProperties(new Properties());
            }
             
            if (!(this.pds instanceof UniversalConnectionPoolAdapter)) {
                LOG.info("Not expected implementation {}, pool sanity check not possible", UniversalConnectionPoolAdapter.class);
            }
            
            final UniversalConnectionPoolAdapter connectionPoolAdapter = (UniversalConnectionPoolAdapter) this.pds;
            connectionPoolAdapter.createUniversalConnectionPool();
            
            this.universalConnectionPool = ucpManager.getConnectionPool(this.connectionPoolName);
            this.universalConnectionPool.start();

            LOG.info("Startup - Initial");
            UniversalConnectionPoolLifeCycleState loopLifeCycleState = UniversalConnectionPoolLifeCycleState.LIFE_CYCLE_STARTING;
            while ((System.currentTimeMillis() - startOracleConnectionProvider) < MAX_STARTUP_WAIT_MILLIS
                    && loopLifeCycleState != UniversalConnectionPoolLifeCycleState.LIFE_CYCLE_RUNNING) {
                LOG.info("Startup - Waited {}ms not ready {}", System.currentTimeMillis() - startOracleConnectionProvider, loopLifeCycleState);
                loopLifeCycleState = this.universalConnectionPool.getLifeCycleState();
                Thread.sleep(500);
            }
            
            final UniversalConnectionPoolLifeCycleState finalLifeCycleState = this.universalConnectionPool.getLifeCycleState();
            if (finalLifeCycleState != UniversalConnectionPoolLifeCycleState.LIFE_CYCLE_RUNNING) {
                LOG.warn("Startup - Wrong State {}, max wait time of {}ms exceeded.", finalLifeCycleState, MAX_STARTUP_WAIT_MILLIS);
            } else {
                LOG.info("Startup - Successfully started within {}ms", System.currentTimeMillis() - startOracleConnectionProvider);
            }
        } catch (final InterruptedException e) {
            LOG.error("Error while initializing the data source, thread was interrupted", e);
            Thread.currentThread().interrupt();
        } catch (final SQLException e) {
            LOG.error("Error while initializing the data source", e);
            throw new HRLDriverException(e);
        } catch (final UniversalConnectionPoolException e) {
            LOG.error("Error while initializing the universal connection pool manager.", e);
            throw new HRLDriverException(e);
        } catch (final Throwable e) {
            LOG.error("Error while initializing the driver.", e);
            throw new HRLDriverException(e);
        }
    }

    public Connection getConnection() throws SQLException {
        if(this.universalConnectionPool.getLifeCycleState() != UniversalConnectionPoolLifeCycleState.LIFE_CYCLE_RUNNING){
            throw new SQLException("Pool is not running, state is " + this.universalConnectionPool.getLifeCycleState());
        }
        return this.pds.getConnection();
    }
    
    public void destroy() throws SQLException {
        if (this.pds != null) {
            this.pds.setInitialPoolSize(0);
            this.pds.setMinPoolSize(0);
            this.pds.setMaxPoolSize(0);
            final UniversalConnectionPoolLifeCycleState lifeCycleState = this.universalConnectionPool.getLifeCycleState();
            try {
                if(lifeCycleState == UniversalConnectionPoolLifeCycleState.LIFE_CYCLE_STOPPED){
                    LOG.info("Connection pool is already in state {}", this.universalConnectionPool.getLifeCycleState());
                } else {
                    LOG.info("Connection pool try to purge with state {}", lifeCycleState);
                    this.universalConnectionPool.purge();
                    LOG.info("Connection pool '{}' successfully purged final state {}", this.connectionPoolName, this.universalConnectionPool.getLifeCycleState());
                }
            } catch (final UniversalConnectionPoolException e) {
                LOG.error("Connection pool {} purge failed with state {}",this.connectionPoolName, lifeCycleState, e);
            } catch (final Throwable e){
                LOG.error("Connection pool {} purge failed with unknown error with state {}", this.connectionPoolName, lifeCycleState, e);
            }
        }
    }
}

依赖差异分析

无法正常运行的依赖配置

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>23.2.0.0</version>
</dependency>
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ucp</artifactId>
    <version>23.2.0.0</version>
</dependency>

可正常运行的依赖配置

<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8-production</artifactId>
    <version>23.2.0.0</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ucp</artifactId>
    <version>23.2.0.0</version>
</dependency>

原因与解决方案

单独引入ojdbc8时,缺少Oracle UCP连接池在JUnit测试环境启动所需的附属依赖(如oraclepki、osdt_core等),这些依赖是连接池完成生命周期切换(从Starting到Running)的必要组件。而ojdbc8-production是一个聚合POM,会自动引入ojdbc8及所有配套的依赖包,确保连接池启动时的依赖完整性。

解决方法:将单独的ojdbc8依赖替换为ojdbc8-production聚合POM,即可在JUnit测试中正常启动连接池。

内容的提问来源于stack exchange,提问作者eisentor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:37:03