package jnpf.limsService.provider; import com.alibaba.druid.pool.DruidDataSource; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; import org.springframework.core.env.Environment; import org.springframework.jdbc.datasource.AbstractDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; /** Holds a lazy, bounded pool without becoming a candidate for business DataSource injection. */ public class LimsElnReadonlyPool implements AutoCloseable { private final Environment environment; private final DataSource businessDataSource; private DruidDataSource pool; private boolean closed; private final DataSource source = new AbstractDataSource() { @Override public Connection getConnection() throws SQLException { return pool().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { throw new SQLException("Source credentials cannot be overridden", "08001"); } }; public LimsElnReadonlyPool(Environment environment, DataSource businessDataSource) { this.environment = environment; this.businessDataSource = businessDataSource; } public DataSource dataSource() { return source; } private synchronized DruidDataSource pool() throws SQLException { if (closed) throw new SQLException("Source pool is closed", "08001"); if (pool != null) return pool; String url = setting("url"); String username = setting("username"); String password = setting("password"); java.util.Properties connectionProperties = new java.util.Properties(); if (url == null && username == null && password == null) { // Always use the configured primary, never a request-scoped dynamic datasource selection. DataSource primary = businessDataSource instanceof DynamicRoutingDataSource ? ((DynamicRoutingDataSource) businessDataSource).getDataSource(null) : businessDataSource; DruidDataSource business; try { business = primary.unwrap(DruidDataSource.class); } catch (SQLException e) { throw new SQLException("Primary source connection settings are unavailable", "08001"); } if (business == null) throw new SQLException("Primary source connection settings are unavailable", "08001"); url = business.getUrl(); username = business.getUsername(); password = business.getPassword(); connectionProperties.putAll(business.getConnectProperties()); } if (url == null || !url.startsWith("jdbc:postgresql://") || username == null || password == null) { throw new SQLException("ELN source read-only connection is not configured", "08001"); } DruidDataSource candidate = new DruidDataSource(); candidate.setName("eln-source-readonly"); candidate.setUrl(url); candidate.setUsername(username); candidate.setPassword(password); candidate.setConnectProperties(connectionProperties); candidate.setDriverClassName("org.postgresql.Driver"); candidate.setInitialSize(0); candidate.setMinIdle(0); candidate.setMaxActive(3); candidate.setMaxWait(2000); candidate.setConnectionErrorRetryAttempts(0); candidate.setBreakAfterAcquireFailure(true); candidate.setConnectTimeout(2000); candidate.setSocketTimeout(7000); candidate.setDefaultReadOnly(true); candidate.setTestOnBorrow(true); candidate.setValidationQuery("SELECT 1"); candidate.setValidationQueryTimeout(2); pool = candidate; return pool; } private String setting(String name) { String value = environment.getProperty("lims.eln-source.readonly." + name); return value == null || value.trim().isEmpty() || value.contains("${") ? null : value; } @Override public synchronized void close() { closed = true; if (pool != null) pool.close(); } }