001 /* 002 @license.text@ 003 */ 004 005 package biz.hammurapi.sql; 006 007 import java.io.PrintWriter; 008 import java.sql.CallableStatement; 009 import java.sql.Connection; 010 import java.sql.DatabaseMetaData; 011 import java.sql.PreparedStatement; 012 import java.sql.SQLException; 013 import java.sql.SQLWarning; 014 import java.sql.Savepoint; 015 import java.sql.Statement; 016 import java.util.ArrayList; 017 import java.util.Collection; 018 import java.util.HashMap; 019 import java.util.Map; 020 021 import javax.sql.DataSource; 022 023 /** 024 * Wraps another datasource for re-using already allocated thread connections. 025 * Maintains one connection per thread/user name/pwd. Connection is allocated on first 026 * getConnection() call. Every next call increments use counter. 027 * Connection closes when it is not used (counter==0). 028 * 029 * @author Pavel Vlasov 030 */ 031 public class ConnectionPerThreadDataSourceFilter implements DataSource { 032 033 // Key for connection without user name and password. 034 private Object key = new Object(); 035 036 private ThreadLocal connectionTL=new ThreadLocal() { 037 protected Object initialValue() { 038 return new HashMap(); 039 } 040 }; 041 042 private DataSource master; 043 044 /** 045 * Constructor 046 * @param master Master data source 047 * @param initConnectionTransaction optional transaction to initialize connection 048 * e.g. set database schema. 049 */ 050 public ConnectionPerThreadDataSourceFilter(DataSource master) { 051 this.master = master; 052 } 053 054 public int getLoginTimeout() throws SQLException { 055 return master.getLoginTimeout(); 056 } 057 058 public void setLoginTimeout(int seconds) throws SQLException { 059 master.setLoginTimeout(seconds); 060 } 061 062 public PrintWriter getLogWriter() throws SQLException { 063 return master.getLogWriter(); 064 } 065 066 public void setLogWriter(PrintWriter out) throws SQLException { 067 master.setLogWriter(out); 068 } 069 070 public Connection getConnection() throws SQLException { 071 Map threadMap = (Map) connectionTL.get(); 072 Connection ret = (Connection) threadMap.get(key); 073 if (ret==null) { 074 ret = new ConnectionWrapper(master.getConnection(), key, threadMap); 075 } 076 return ret; 077 } 078 079 public Connection getConnection(String user, final String password) throws SQLException { 080 Collection key = new ArrayList(); 081 key.add(user); 082 key.add(password); 083 Map threadMap = (Map) connectionTL.get(); 084 Connection ret = (Connection) threadMap.get(key); 085 if (ret==null) { 086 ret = new ConnectionWrapper(master.getConnection(), key, threadMap); 087 } 088 return ret; 089 } 090 091 private class ConnectionWrapper implements Connection { 092 Connection master; 093 094 private ConnectionWrapper(Connection master, Object key, Map threadMap) { 095 super(); 096 this.master = master; 097 this.key = key; 098 this.threadMap = threadMap; 099 } 100 101 public void clearWarnings() throws SQLException { 102 master.clearWarnings(); 103 } 104 public void close() throws SQLException { 105 counter--; 106 if (counter==0) { 107 master.close(); 108 master = null; 109 threadMap.remove(key); 110 } 111 } 112 public void commit() throws SQLException { 113 master.commit(); 114 } 115 public Statement createStatement() throws SQLException { 116 return master.createStatement(); 117 } 118 public Statement createStatement(int resultSetType, 119 int resultSetConcurrency, int resultSetHoldability) 120 throws SQLException { 121 return master.createStatement(resultSetType, resultSetConcurrency, 122 resultSetHoldability); 123 } 124 public Statement createStatement(int resultSetType, 125 int resultSetConcurrency) throws SQLException { 126 return master.createStatement(resultSetType, resultSetConcurrency); 127 } 128 public boolean getAutoCommit() throws SQLException { 129 return master.getAutoCommit(); 130 } 131 public String getCatalog() throws SQLException { 132 return master.getCatalog(); 133 } 134 public int getHoldability() throws SQLException { 135 return master.getHoldability(); 136 } 137 public DatabaseMetaData getMetaData() throws SQLException { 138 return master.getMetaData(); 139 } 140 public int getTransactionIsolation() throws SQLException { 141 return master.getTransactionIsolation(); 142 } 143 public Map getTypeMap() throws SQLException { 144 return master.getTypeMap(); 145 } 146 public SQLWarning getWarnings() throws SQLException { 147 return master.getWarnings(); 148 } 149 public boolean isClosed() throws SQLException { 150 return counter==0 || (master!=null && master.isClosed()); 151 } 152 public boolean isReadOnly() throws SQLException { 153 return master.isReadOnly(); 154 } 155 public String nativeSQL(String sql) throws SQLException { 156 return master.nativeSQL(sql); 157 } 158 public CallableStatement prepareCall(String sql, int resultSetType, 159 int resultSetConcurrency, int resultSetHoldability) 160 throws SQLException { 161 return master.prepareCall(sql, resultSetType, resultSetConcurrency, 162 resultSetHoldability); 163 } 164 public CallableStatement prepareCall(String sql, int resultSetType, 165 int resultSetConcurrency) throws SQLException { 166 return master.prepareCall(sql, resultSetType, resultSetConcurrency); 167 } 168 public CallableStatement prepareCall(String sql) throws SQLException { 169 return master.prepareCall(sql); 170 } 171 public PreparedStatement prepareStatement(String sql, 172 int resultSetType, int resultSetConcurrency, 173 int resultSetHoldability) throws SQLException { 174 return master.prepareStatement(sql, resultSetType, 175 resultSetConcurrency, resultSetHoldability); 176 } 177 public PreparedStatement prepareStatement(String sql, 178 int resultSetType, int resultSetConcurrency) 179 throws SQLException { 180 return master.prepareStatement(sql, resultSetType, 181 resultSetConcurrency); 182 } 183 public PreparedStatement prepareStatement(String sql, 184 int autoGeneratedKeys) throws SQLException { 185 return master.prepareStatement(sql, autoGeneratedKeys); 186 } 187 public PreparedStatement prepareStatement(String sql, 188 int[] columnIndexes) throws SQLException { 189 return master.prepareStatement(sql, columnIndexes); 190 } 191 public PreparedStatement prepareStatement(String sql, 192 String[] columnNames) throws SQLException { 193 return master.prepareStatement(sql, columnNames); 194 } 195 public PreparedStatement prepareStatement(String sql) 196 throws SQLException { 197 return master.prepareStatement(sql); 198 } 199 public void releaseSavepoint(Savepoint savepoint) throws SQLException { 200 master.releaseSavepoint(savepoint); 201 } 202 public void rollback() throws SQLException { 203 master.rollback(); 204 } 205 public void rollback(Savepoint savepoint) throws SQLException { 206 master.rollback(savepoint); 207 } 208 public void setAutoCommit(boolean autoCommit) throws SQLException { 209 master.setAutoCommit(autoCommit); 210 } 211 public void setCatalog(String catalog) throws SQLException { 212 master.setCatalog(catalog); 213 } 214 public void setHoldability(int holdability) throws SQLException { 215 master.setHoldability(holdability); 216 } 217 public void setReadOnly(boolean readOnly) throws SQLException { 218 master.setReadOnly(readOnly); 219 } 220 public Savepoint setSavepoint() throws SQLException { 221 return master.setSavepoint(); 222 } 223 public Savepoint setSavepoint(String name) throws SQLException { 224 return master.setSavepoint(name); 225 } 226 public void setTransactionIsolation(int level) throws SQLException { 227 master.setTransactionIsolation(level); 228 } 229 public void setTypeMap(Map arg0) throws SQLException { 230 master.setTypeMap(arg0); 231 } 232 233 int counter=1; 234 Object key; 235 Map threadMap; 236 } 237 238 } 239