1 2 12 package com.versant.core.jdbc.conn; 13 14 import java.sql.SQLException ; 15 import java.util.Iterator ; 16 import java.util.HashMap ; 17 import java.util.Map ; 18 19 import com.versant.core.common.Debug; 20 import com.versant.core.common.BindingSupportImpl; 21 22 25 public final class PreparedStatementPool { 26 27 private final LoggingConnection con; 28 private final HashMap keys = new HashMap (); private Key head; private Key tail; private int max; 32 private int numActive; 33 private int numIdle; 34 35 public PreparedStatementPool(LoggingConnection con, int max) { 36 this.con = con; 37 this.max = max; 38 } 39 40 public LoggingConnection getCon() { 41 return con; 42 } 43 44 public int getMax() { 45 return max; 46 } 47 48 51 public int getNumIdle() { 52 return numIdle; 53 } 54 55 58 public int getNumActive() { 59 return numActive; 60 } 61 62 65 public int getSize() { 66 return numIdle + numActive; 67 } 68 69 74 public PooledPreparedStatement borrowPS(Key pkey) throws SQLException { 75 Key key = (Key)keys.get(pkey); 76 if (key == null) { 77 keys.put(key = pkey, pkey); 78 } else { 79 removeFromLRUList(key); 80 } 81 addToHeadOfLRUList(key); 82 PooledPreparedStatement ps = key.removePsFromList(); 83 if (ps == null) { 84 ps = con.prepareStatementImp(key.getSql(), key.getResultSetType(), 85 key.getResultSetConcurrency(), key); 86 } else { 87 --numIdle; 88 } 89 ++key.activeCount; 90 ++numActive; 91 if (max > 0) { 92 closeExcessStatements(); 93 } 94 if (Debug.DEBUG) { 95 check(); 96 } 97 return ps; 98 } 99 100 103 public void returnPS(PooledPreparedStatement ps) { 104 Key key = ps.getKey(); 105 key.addPsToList(ps); 106 --key.activeCount; 107 ++numIdle; 108 --numActive; 109 if (max > 0) { 110 closeExcessStatements(); 111 } 112 if (Debug.DEBUG) { 113 check(); 114 } 115 } 116 117 private void closeExcessStatements() { 118 int toClose = (numActive + numIdle) - max; 119 if (toClose > numIdle) { 120 toClose = numIdle; 121 } 122 for (Key key = tail; toClose > 0; ) { 123 for (;;) { 124 PooledPreparedStatement ps = key.removePsFromList(); 125 if (ps == null) { 126 break; 127 } 128 try { 129 ps.closeRealStatement(); 130 } catch (SQLException e) { 131 } 133 --numIdle; 134 if (--toClose == 0) { 135 break; 136 } 137 } 138 if (key.psList == null) { 139 if (key.activeCount == 0) { 140 Key next = key.next; 141 removeFromLRUList(key); 142 keys.remove(key); 143 key = next; 144 } else { 145 key = key.next; 146 } 147 } 148 } 149 if (Debug.DEBUG) { 150 check(); 151 } 152 } 153 154 private void check() { 155 try { 156 int c = 0; 158 for (Iterator i = keys.keySet().iterator(); i.hasNext(); ) { 159 Key key = (Key)i.next(); 160 for (PooledPreparedStatement ps = key.psList; ps != null; ++c) { 161 if (ps == ps.next) { 162 throw BindingSupportImpl.getInstance().internal( 163 "ps == ps.next for key " + key); 164 } 165 ps = ps.next; 166 } 167 } 168 if (numIdle != c) { 169 throw BindingSupportImpl.getInstance().internal("numIdle is " + 170 numIdle + " but there are " + c + " idle PS'es in map"); 171 } 172 c = 0; 176 int totActive = 0; 177 Map dupMap = new HashMap (); 178 for (Key key = tail; key != null; key = key.next, ++c) { 179 if (key.next != null) { 180 if (key.next.prev != key) { 181 throw BindingSupportImpl.getInstance().internal( 182 "key.next.prev != key"); 183 } 184 } 185 Key pkey = (Key)dupMap.get(key); 186 if (pkey != null) { 187 throw BindingSupportImpl.getInstance().internal( 188 "Dup key in LRU list: " + pkey); 189 } 190 dupMap.put(key, key); 191 if (!keys.containsKey(key)) { 192 throw BindingSupportImpl.getInstance().internal( 193 "Key in LRU list not in map: " + key); 194 } 195 totActive += key.activeCount; 196 } 197 if (c != keys.size()) { 198 throw BindingSupportImpl.getInstance().internal( 199 "There are " + c + " keys in the LRU list and " + 200 keys.size() + " in the keys map"); 201 } 202 if (numActive != totActive) { 203 throw BindingSupportImpl.getInstance().internal("numActive is " + 204 numIdle + " but there are " + c + " active PS'es in keys"); 205 } 206 } catch (RuntimeException e) { 207 System.out.println("PreparedStatementPool check failed: " + e); 208 dump(); 209 e.printStackTrace(System.out); 210 throw e; 211 } 212 } 213 214 217 private void removeFromLRUList(Key key) { 218 if (key.prev != null) { 219 key.prev.next = key.next; 220 } else { 221 tail = key.next; 222 } 223 if (key.next != null) { 224 key.next.prev = key.prev; 225 } else { 226 head = key.prev; 227 } 228 } 229 230 234 private void addToHeadOfLRUList(Key key) { 235 key.next = null; 236 key.prev = head; 237 if (head != null) head.next = key; 238 head = key; 239 if (tail == null) tail = key; 240 } 241 242 245 public void dump() { 246 if (Debug.DEBUG) { 247 System.out.println("=== keys ==="); 248 for (Iterator i = keys.keySet().iterator(); i.hasNext(); ) { 249 Key key = (Key)i.next(); 250 System.out.println(key.toDumpString()); 251 } 252 System.out.println("=== LRU list ==="); 253 for (Key key = tail; key != null; key = key.next) { 254 System.out.println(key); 255 } 256 System.out.println("---"); 257 } 258 } 259 260 265 public static final class Key { 266 267 private final String sql; 268 private final int resultSetType; 269 private final int resultSetConcurrency; 270 271 private PooledPreparedStatement psList; 272 private int activeCount; 273 private Key prev; 274 private Key next; 275 276 public Key(String sql, int resultSetType, int resultSetConcurrency) { 277 if (sql == null) { 278 throw new NullPointerException ("sql is null"); 279 } 280 this.sql = sql; 281 this.resultSetType = resultSetType; 282 this.resultSetConcurrency = resultSetConcurrency; 283 } 284 285 public Key(String sql) { 286 if (sql == null) { 287 throw new NullPointerException ("sql is null"); 288 } 289 this.sql = sql; 290 resultSetType = 0; 291 resultSetConcurrency = 0; 292 } 293 294 public String getSql() { 295 return sql; 296 } 297 298 public int getResultSetType() { 299 return resultSetType; 300 } 301 302 public int getResultSetConcurrency() { 303 return resultSetConcurrency; 304 } 305 306 public boolean equals(Object o) { 307 if (!(o instanceof Key)) return false; 308 final Key psKey = (Key)o; 309 return resultSetConcurrency == psKey.resultSetConcurrency 310 && resultSetType == psKey.resultSetType 311 && sql.equals(psKey.sql); 312 } 313 314 public int hashCode() { 315 return sql.hashCode() + resultSetType + resultSetConcurrency; 316 } 317 318 public String toString() { 319 StringBuffer s = new StringBuffer (); 320 s.append(Integer.toHexString(sql.hashCode())); 321 for (int i = 9 - s.length(); i > 0; i--) { 322 s.append(' '); 323 } 324 if (sql.length() > 40) { 325 s.append(sql.substring(0, 37)); 326 s.append("..."); 327 } else { 328 s.append(sql); 329 } 330 return s.toString(); 331 } 332 333 public String toDumpString() { 334 StringBuffer s = new StringBuffer (); 335 s.append(toString()); 336 for (PooledPreparedStatement ps = psList; ps != null; ) { 337 s.append(" -> "); 338 s.append(ps); 339 if (ps == ps.next) { 340 s.append("*** LOOP ps == ps.next ***"); 341 break; 342 } 343 ps = ps.next; 344 } 345 return s.toString(); 346 } 347 348 351 public boolean isPsListEmpty() { 352 return psList == null; 353 } 354 355 359 public PooledPreparedStatement removePsFromList() { 360 if (psList == null) { 361 return null; 362 } 363 PooledPreparedStatement ps = psList; 364 psList = ps.next; 365 ps.next = null; 366 return ps; 367 } 368 369 372 public void addPsToList(PooledPreparedStatement ps) { 373 ps.next = psList; 374 psList = ps; 375 } 376 } 377 378 } 379 | Popular Tags |