diff --git a/src/main/java/com/alibaba/druid/pool/DataSourceAdapter.java b/src/main/java/com/alibaba/druid/pool/DataSourceAdapter.java index 3e58dca163..07973b0e16 100644 --- a/src/main/java/com/alibaba/druid/pool/DataSourceAdapter.java +++ b/src/main/java/com/alibaba/druid/pool/DataSourceAdapter.java @@ -6,7 +6,8 @@ import java.sql.SQLException; import javax.sql.DataSource; -public class DataSourceAdapter implements DataSource { +public class DataSourceAdapter extends WrapperAdapter implements DataSource { + private int loginTimeout = 0; private PrintWriter logWriter = new PrintWriter(System.out); @@ -33,33 +34,6 @@ public class DataSourceAdapter implements DataSource { return loginTimeout; } - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - if (iface == null) { - return false; - } - - if (iface.isInstance(this)) { - return true; - } - - return false; - } - - @SuppressWarnings("unchecked") - @Override - public T unwrap(Class iface) throws SQLException { - if (iface == null) { - return null; - } - - if (iface.isInstance(this)) { - return (T) this; - } - - return null; - } - @Override public Connection getConnection() throws SQLException { throw new UnsupportedOperationException(); diff --git a/src/main/java/com/alibaba/druid/pool/WrapperAdapter.java b/src/main/java/com/alibaba/druid/pool/WrapperAdapter.java new file mode 100644 index 0000000000..ca8e0c045e --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/WrapperAdapter.java @@ -0,0 +1,38 @@ +package com.alibaba.druid.pool; + +import java.sql.SQLException; +import java.sql.Wrapper; + +public class WrapperAdapter implements Wrapper { + + public WrapperAdapter(){ + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + if (iface == null) { + return false; + } + + if (iface.isInstance(this)) { + return true; + } + + return false; + } + + @SuppressWarnings("unchecked") + @Override + public T unwrap(Class iface) throws SQLException { + if (iface == null) { + return null; + } + + if (iface.isInstance(this)) { + return (T) this; + } + + return null; + } + +} diff --git a/src/main/java/com/alibaba/druid/pool/ha/HAConnection.java b/src/main/java/com/alibaba/druid/pool/ha/HAConnection.java new file mode 100644 index 0000000000..8daa7ffd6a --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/ha/HAConnection.java @@ -0,0 +1,546 @@ +package com.alibaba.druid.pool.ha; + +import java.sql.Array; +import java.sql.Blob; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.NClob; +import java.sql.PreparedStatement; +import java.sql.SQLClientInfoException; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.SQLXML; +import java.sql.Savepoint; +import java.sql.Statement; +import java.sql.Struct; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.alibaba.druid.pool.WrapperAdapter; +import com.alibaba.druid.proxy.jdbc.CallableStatementProxyImpl; +import com.alibaba.druid.proxy.jdbc.ConnectionProxy; +import com.alibaba.druid.proxy.jdbc.DataSourceProxy; +import com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl; + +public class HAConnection extends WrapperAdapter implements Connection, ConnectionProxy { + + private final HADataSource haDataSource; + + private Connection conn; + + private final int id; + + private Boolean autoCommit = null; + private Boolean readOnly = null; + private String catalog = null; + private Integer transactionLeval = null; + private Map> typeMap = null; + private Integer holdability = null; + + private Properties clientInfo = null; + + private Map attributes = null; + + private Date connectedTime = new Date(); + + public HAConnection(HADataSource haDataSource, int id){ + this.haDataSource = haDataSource; + this.id = id; + } + + public HADataSource getHaDataSource() { + return haDataSource; + } + + private void checkConnection(String sql) throws SQLException { + if (conn == null) { + conn = haDataSource.getConnectionInternal(id); + } + } + + @Override + public String nativeSQL(String sql) throws SQLException { + checkConnection(sql); + + return conn.nativeSQL(sql); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + if (conn != null) { + conn.setAutoCommit(autoCommit); + } else { + this.autoCommit = Boolean.valueOf(autoCommit); + } + } + + @Override + public boolean getAutoCommit() throws SQLException { + if (conn != null) { + return conn.getAutoCommit(); + } + + if (this.autoCommit != null) { + return this.autoCommit.booleanValue(); + } + + return false; + } + + @Override + public void commit() throws SQLException { + if (conn != null) { + conn.commit(); + } + } + + @Override + public void rollback() throws SQLException { + if (conn != null) { + conn.rollback(); + } + } + + @Override + public void close() throws SQLException { + if (conn != null) { + conn.close(); + } + } + + @Override + public boolean isClosed() throws SQLException { + if (conn != null) { + return conn.isClosed(); + } + + return false; + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + if (conn != null) { + return conn.getMetaData(); + } + + throw new SQLException("connection not init"); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + if (conn != null) { + conn.setReadOnly(readOnly); + } else { + this.readOnly = Boolean.valueOf(readOnly); + } + } + + @Override + public boolean isReadOnly() throws SQLException { + if (conn != null) { + return conn.isReadOnly(); + } + + if (this.readOnly != null) { + return this.readOnly.booleanValue(); + } + + return false; + } + + @Override + public void setCatalog(String catalog) throws SQLException { + if (conn != null) { + conn.setCatalog(catalog); + } else { + this.catalog = catalog; + } + } + + @Override + public String getCatalog() throws SQLException { + if (conn != null) { + return conn.getCatalog(); + } + + return this.catalog; + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + if (conn != null) { + conn.setTransactionIsolation(level); + } else { + this.transactionLeval = level; + } + } + + @Override + public int getTransactionIsolation() throws SQLException { + if (conn != null) { + return conn.getTransactionIsolation(); + } + + if (this.transactionLeval != null) { + return this.transactionLeval; + } + + return TRANSACTION_NONE; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + if (conn != null) { + return conn.getWarnings(); + } + + return null; + } + + @Override + public void clearWarnings() throws SQLException { + if (conn != null) { + conn.clearWarnings(); + } + } + + @Override + public Map> getTypeMap() throws SQLException { + if (conn != null) { + return conn.getTypeMap(); + } + + return typeMap; + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + if (conn != null) { + conn.setTypeMap(map); + } else { + this.typeMap = map; + } + } + + @Override + public void setHoldability(int holdability) throws SQLException { + if (conn != null) { + conn.setHoldability(holdability); + } else { + this.holdability = holdability; + } + } + + @Override + public int getHoldability() throws SQLException { + if (conn != null) { + return conn.getHoldability(); + } + + if (this.holdability != null) { + return this.holdability; + } + + return 0; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + if (conn != null) { + return conn.setSavepoint(); + } + + throw new SQLException("connection not init"); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + if (conn != null) { + return conn.setSavepoint(name); + } + + throw new SQLException("connection not init"); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + if (conn != null) { + conn.rollback(savepoint); + } + + throw new SQLException("connection not init"); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + if (conn != null) { + conn.releaseSavepoint(savepoint); + } + + throw new SQLException("connection not init"); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + long stmtId = haDataSource.createStatementId(); + HAStatement stmt = new HAStatement(this, stmtId, resultSetType, resultSetConcurrency); + return stmt; + } + + @Override + public Statement createStatement() throws SQLException { + long stmtId = haDataSource.createStatementId(); + HAStatement stmt = new HAStatement(this, stmtId); + return stmt; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + checkConnection(sql); + + CallableStatement stmt = conn.prepareCall(sql); + long stmtId = haDataSource.createStatementId(); + + return new CallableStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql, resultSetType, resultSetConcurrency); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + checkConnection(sql); + + CallableStatement stmt = conn.prepareCall(sql, resultSetType, resultSetConcurrency); + long stmtId = haDataSource.createStatementId(); + + return new CallableStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + long stmtId = haDataSource.createStatementId(); + HAStatement stmt = new HAStatement(this, stmtId, resultSetType, resultSetConcurrency, resultSetHoldability); + return stmt; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + checkConnection(sql); + + CallableStatement stmt = conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + long stmtId = haDataSource.createStatementId(); + + return new CallableStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql, autoGeneratedKeys); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql, columnIndexes); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + checkConnection(sql); + + PreparedStatement stmt = conn.prepareStatement(sql, columnNames); + long stmtId = haDataSource.createStatementId(); + + return new PreparedStatementProxyImpl(this, stmt, sql, stmtId); + } + + @Override + public Clob createClob() throws SQLException { + if (conn != null) { + return conn.createClob(); + } + + throw new SQLException("connection not init"); + } + + @Override + public Blob createBlob() throws SQLException { + if (conn != null) { + return conn.createBlob(); + } + + throw new SQLException("connection not init"); + } + + @Override + public NClob createNClob() throws SQLException { + if (conn != null) { + return conn.createNClob(); + } + + throw new SQLException("connection not init"); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + if (conn != null) { + return conn.createSQLXML(); + } + + throw new SQLException("connection not init"); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + if (conn != null) { + return conn.isValid(timeout); + } + + return true; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + if (conn != null) { + conn.setClientInfo(name, value); + return; + } + + if (clientInfo == null) { + clientInfo = new Properties(); + } + clientInfo.setProperty(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + if (conn != null) { + conn.setClientInfo(properties); + return; + } + + this.clientInfo = properties; + } + + @Override + public String getClientInfo(String name) throws SQLException { + if (conn != null) { + return conn.getClientInfo(name); + } + + if (clientInfo == null) { + return null; + } + + return clientInfo.getProperty(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + if (conn != null) { + return conn.getClientInfo(); + } + + return clientInfo; + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + if (conn != null) { + return conn.createArrayOf(typeName, elements); + } + + throw new SQLException("connection not init"); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + if (conn != null) { + return conn.createStruct(typeName, attributes); + } + + throw new SQLException("connection not init"); + } + + // ///// + + @Override + public long getId() { + return id; + } + + @Override + public Object getRawObject() { + return conn; + } + + @Override + public Map getAttributes() { + if (attributes == null) { + attributes = new HashMap(); + } + return attributes; + } + + @Override + public Connection getConnectionRaw() { + return conn; + } + + @Override + public Properties getProperties() { + return haDataSource.getProperties(); + } + + @Override + public DataSourceProxy getDirectDataSource() { + throw new UnsupportedOperationException(); + } + + @Override + public Date getConnectedTime() { + return connectedTime; + } + +} diff --git a/src/main/java/com/alibaba/druid/pool/ha/HADataSource.java b/src/main/java/com/alibaba/druid/pool/ha/HADataSource.java index a4f166ab95..c52d9cd663 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/HADataSource.java +++ b/src/main/java/com/alibaba/druid/pool/ha/HADataSource.java @@ -16,7 +16,8 @@ public class HADataSource extends MultiDataSource implements DataSource { private final static Log LOG = LogFactory.getLog(HADataSource.class); - private final AtomicInteger requestCount = new AtomicInteger(); + private final AtomicInteger connectionIdSeed = new AtomicInteger(); + private final AtomicInteger statementIdSeed = new AtomicInteger(); private final AtomicInteger indexErrorCount = new AtomicInteger(); protected final List notAvailableDatasources = new CopyOnWriteArrayList(); @@ -26,8 +27,17 @@ public class HADataSource extends MultiDataSource implements DataSource { @Override public Connection getConnection() throws SQLException { + int requestNumber = connectionIdSeed.getAndIncrement(); + + return new HAConnection(this, requestNumber); + } + + public int createStatementId() { + return statementIdSeed.getAndIncrement(); + } + + public Connection getConnectionInternal(int requestNumber) throws SQLException { int tryCount = 0; - int requestNumber = requestCount.getAndIncrement(); for (;;) { int size = dataSources.size(); @@ -76,7 +86,7 @@ public class HADataSource extends MultiDataSource implements DataSource { return conn; } } - + void handleNotAwailableDatasource(DruidDataSource dataSource) { boolean removed = dataSources.remove(dataSource); if (removed) { diff --git a/src/main/java/com/alibaba/druid/pool/ha/HAStatement.java b/src/main/java/com/alibaba/druid/pool/ha/HAStatement.java new file mode 100644 index 0000000000..668c0f8bc9 --- /dev/null +++ b/src/main/java/com/alibaba/druid/pool/ha/HAStatement.java @@ -0,0 +1,290 @@ +package com.alibaba.druid.pool.ha; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; + +import com.alibaba.druid.pool.WrapperAdapter; + +public class HAStatement extends WrapperAdapter implements Statement { + + private final HAConnection conn; + private final long id; + private Integer resultSetType; + private Integer resultSetConcurrency; + private Integer resultSetHoldability; + + public HAStatement(HAConnection conn, long id){ + super(); + this.conn = conn; + this.id = id; + } + + public HAStatement(HAConnection conn, long id, int resultSetType, int resultSetConcurrency){ + super(); + this.conn = conn; + this.id = id; + this.resultSetType = resultSetType; + this.resultSetConcurrency = resultSetConcurrency; + } + + public HAStatement(HAConnection conn, long id, int resultSetType, int resultSetConcurrency, int resultSetHoldability){ + super(); + this.conn = conn; + this.id = id; + this.resultSetType = resultSetType; + this.resultSetConcurrency = resultSetConcurrency; + this.resultSetHoldability = resultSetHoldability; + } + + public HAConnection getConn() { + return conn; + } + + public long getId() { + return id; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public int executeUpdate(String sql) throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void close() throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int getMaxFieldSize() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int getMaxRows() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setMaxRows(int max) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int getQueryTimeout() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public void cancel() throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public SQLWarning getWarnings() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void clearWarnings() throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public void setCursorName(String name) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public boolean execute(String sql) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getResultSet() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public int getUpdateCount() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean getMoreResults() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int getFetchDirection() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int getFetchSize() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getResultSetConcurrency() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getResultSetType() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void addBatch(String sql) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public void clearBatch() throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public int[] executeBatch() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Connection getConnection() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isClosed() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + // TODO Auto-generated method stub + + } + + @Override + public boolean isPoolable() throws SQLException { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java index 10ab7c6144..00b1a36b5f 100644 --- a/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java +++ b/src/main/java/com/alibaba/druid/pool/ha/MultiDataSource.java @@ -5,6 +5,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Properties; import com.alibaba.druid.pool.DataSourceAdapter; import com.alibaba.druid.pool.DruidDataSource; @@ -13,6 +14,8 @@ public abstract class MultiDataSource extends DataSourceAdapter { protected ArrayList dataSources; + private Properties properties = new Properties(); + public MultiDataSource(){ dataSources = new ArrayList(); } @@ -22,7 +25,12 @@ public abstract class MultiDataSource extends DataSourceAdapter { } public void setDataSources(List dataSources) { - this.dataSources = new ArrayList(dataSources);; + this.dataSources = new ArrayList(dataSources); + ; + } + + public Properties getProperties() { + return properties; } @Override