1 22 package org.jboss.ejb.plugins.cmp.jdbc2.schema; 23 24 import org.jboss.system.ServiceMBeanSupport; 25 import org.jboss.metadata.MetaData; 26 import org.jboss.deployment.DeploymentException; 27 import org.w3c.dom.Element ; 28 29 import javax.transaction.Transaction ; 30 import java.util.Map ; 31 import java.util.HashMap ; 32 33 34 41 public class TableCache 42 extends ServiceMBeanSupport 43 implements Cache, TableCacheMBean 44 { 45 private Cache.Listener listener = Cache.Listener.NOOP; 46 private final Map rowsById; 47 private CachedRow head; 48 private CachedRow tail; 49 private int maxCapacity; 50 private final int minCapacity; 51 52 private boolean locked; 53 54 private final int partitionIndex; 55 56 public TableCache(int partitionIndex, int initialCapacity, int maxCapacity) 57 { 58 this.maxCapacity = maxCapacity; 59 this.minCapacity = initialCapacity; 60 rowsById = new HashMap (initialCapacity); 61 this.partitionIndex = partitionIndex; 62 } 63 64 public TableCache(Element conf) throws DeploymentException 65 { 66 String str = MetaData.getOptionalChildContent(conf, "min-capacity"); 67 minCapacity = (str == null ? 1000 : Integer.parseInt(str)); 68 rowsById = new HashMap (minCapacity); 69 70 str = MetaData.getOptionalChildContent(conf, "max-capacity"); 71 maxCapacity = (str == null ? 10000 : Integer.parseInt(str)); 72 73 this.partitionIndex = 0; 74 } 75 76 79 public void registerListener(Cache.Listener listener) 80 { 81 if(log.isTraceEnabled() && getServiceName() != null) 82 { 83 log.trace("registered listener for " + getServiceName()); 84 } 85 this.listener = listener; 86 } 87 88 91 public int size() 92 { 93 lock(); 94 try 95 { 96 return rowsById.size(); 97 } 98 finally 99 { 100 unlock(); 101 } 102 } 103 104 107 public int getMaxCapacity() 108 { 109 return maxCapacity; 110 } 111 112 115 public void setMaxCapacity(int maxCapacity) 116 { 117 this.maxCapacity = maxCapacity; 118 } 119 120 123 public int getMinCapacity() 124 { 125 return minCapacity; 126 } 127 128 public synchronized void lock() 129 { 130 if(locked) 131 { 132 long start = System.currentTimeMillis(); 133 while(locked) 134 { 135 try 136 { 137 wait(); 138 } 139 catch(InterruptedException e) 140 { 141 } 142 } 143 144 listener.contention(partitionIndex, System.currentTimeMillis() - start); 145 } 146 locked = true; 147 } 148 149 public void lock(Object key) 150 { 151 lock(); 152 } 153 154 public synchronized void unlock() 155 { 156 if(!locked) 157 { 158 throw new IllegalStateException ("The instance is not locked!"); 159 } 160 locked = false; 161 notify(); 162 } 163 164 public void unlock(Object key) 165 { 166 unlock(); 167 } 168 169 public Object [] getFields(Object pk) 170 { 171 Object [] fields; 172 CachedRow row = (CachedRow) rowsById.get(pk); 173 if(row != null && row.locker == null) 174 { 175 promoteRow(row); 176 fields = new Object [row.fields.length]; 177 System.arraycopy(row.fields, 0, fields, 0, fields.length); 178 listener.hit(partitionIndex); 179 } 180 else 181 { 182 fields = null; 183 listener.miss(partitionIndex); 184 } 185 return fields; 186 } 187 188 public Object [] getRelations(Object pk) 189 { 190 Object [] relations; 191 CachedRow row = (CachedRow) rowsById.get(pk); 192 if(row != null && row.relations != null && row.locker == null) 193 { 194 promoteRow(row); 195 relations = new Object [row.relations.length]; 196 System.arraycopy(row.relations, 0, relations, 0, relations.length); 197 } 198 else 199 { 200 relations = null; 201 } 202 return relations; 203 } 204 205 public void put(Transaction tx, Object pk, Object [] fields, Object [] relations) 206 { 207 CachedRow row = (CachedRow) rowsById.get(pk); 208 if(row == null) { 210 Object [] fieldsCopy = new Object [fields.length]; 211 System.arraycopy(fields, 0, fieldsCopy, 0, fields.length); 212 row = new CachedRow(pk, fieldsCopy); 213 214 if(relations != null) 215 { 216 Object [] relationsCopy = new Object [relations.length]; 217 System.arraycopy(relations, 0, relationsCopy, 0, relations.length); 218 row.relations = relationsCopy; 219 } 220 221 rowsById.put(pk, row); 222 223 if(head == null) 224 { 225 head = row; 226 tail = row; 227 } 228 else 229 { 230 head.prev = row; 231 row.next = head; 232 head = row; 233 } 234 } 235 else if(row.locker == null || row.locker.equals(tx)) { 237 promoteRow(row); 238 System.arraycopy(fields, 0, row.fields, 0, fields.length); 239 240 if(relations != null) 241 { 242 if(row.relations == null) 243 { 244 row.relations = new Object [relations.length]; 245 } 246 System.arraycopy(relations, 0, row.relations, 0, relations.length); 247 } 248 249 row.lastUpdated = System.currentTimeMillis(); 250 row.locker = null; 251 } 252 253 CachedRow victim = tail; 254 while(rowsById.size() > maxCapacity && victim != null) 255 { 256 CachedRow nextVictim = victim.prev; 257 if(victim.locker == null) 258 { 259 dereference(victim); 260 rowsById.remove(victim.pk); 261 listener.eviction(partitionIndex, row.pk, rowsById.size()); 262 } 263 victim = nextVictim; 264 } 265 } 266 267 public void ageOut(long lastUpdated) 268 { 269 CachedRow victim = tail; 270 while(victim != null && victim.lastUpdated < lastUpdated) 271 { 272 CachedRow nextVictim = victim.prev; 273 if(victim.locker == null) 274 { 275 dereference(victim); 276 rowsById.remove(victim.pk); 277 listener.eviction(partitionIndex, victim.pk, rowsById.size()); 278 } 279 victim = nextVictim; 280 } 281 } 282 283 public void remove(Transaction tx, Object pk) 284 { 285 CachedRow row = (CachedRow) rowsById.remove(pk); 286 if(row == null || row.locker != null && !tx.equals(row.locker)) 287 { 288 String msg = "removal of " + 289 pk + 290 " rejected for " + 291 tx + 292 ": " + 293 (row == null ? "the entry could not be found" : "the entry is locked for update by " + row.locker); 294 throw new RemoveException(msg); 295 } 296 297 dereference(row); 298 row.locker = null; 299 } 300 301 public boolean contains(Transaction tx, Object pk) 302 { 303 CachedRow row = (CachedRow) rowsById.get(pk); 304 return row != null && (row.locker == null || tx.equals(row.locker)); 305 } 306 307 public void lockForUpdate(Transaction tx, Object pk) throws Exception 308 { 309 CachedRow row = (CachedRow) rowsById.get(pk); 310 if(row != null) 311 { 312 if(row.locker != null && !tx.equals(row.locker)) 313 { 314 throw new Exception ("lock acquisition rejected for " + 315 tx + 316 ", the entry is locked for update by " + row.locker + ", id=" + pk); 317 } 318 row.locker = tx; 319 } 320 } 322 323 public void releaseLock(Transaction tx, Object pk) throws Exception 324 { 325 CachedRow row = (CachedRow) rowsById.get(pk); 326 if(row != null) 327 { 328 if(!tx.equals(row.locker)) 329 { 330 throw new Exception ("rejected to release lock for " + 331 tx + 332 ", the entry is locked for update by " + row.locker + ", id=" + pk); 333 } 334 row.locker = null; 335 } 336 } 338 339 public void flush() 340 { 341 this.rowsById.clear(); 342 this.head = null; 343 this.tail = null; 344 } 345 346 public String toString() 347 { 348 StringBuffer buf = new StringBuffer (); 349 buf.append('['); 350 351 try 352 { 353 lock(); 354 355 CachedRow cursor = head; 356 while(cursor != null) 357 { 358 buf.append('(') 359 .append(cursor.pk) 360 .append('|'); 361 362 for(int i = 0; i < cursor.fields.length; ++i) 363 { 364 if(i > 0) 365 { 366 buf.append(','); 367 } 368 369 buf.append(cursor.fields[i]); 370 } 371 372 buf.append(')'); 373 374 cursor = cursor.next; 375 } 376 } 377 finally 378 { 379 unlock(); 380 } 381 382 buf.append(']'); 383 return buf.toString(); 384 } 385 386 388 private void dereference(CachedRow row) 389 { 390 CachedRow next = row.next; 391 CachedRow prev = row.prev; 392 393 if(row == head) 394 { 395 head = next; 396 } 397 398 if(row == tail) 399 { 400 tail = prev; 401 } 402 403 if(next != null) 404 { 405 next.prev = prev; 406 } 407 408 if(prev != null) 409 { 410 prev.next = next; 411 } 412 413 row.next = null; 414 row.prev = null; 415 } 416 417 private void promoteRow(CachedRow row) 418 { 419 if(head == null) { 421 head = row; 422 tail = row; 423 } 424 else if(row == head) { 426 } 427 else if(row == tail) { 429 tail = row.prev; 430 tail.next = null; 431 432 row.prev = null; 433 row.next = head; 434 435 head.prev = row; 436 head = row; 437 } 438 else { 440 CachedRow next = row.next; 441 CachedRow prev = row.prev; 442 443 if(prev != null) 444 { 445 prev.next = next; 446 } 447 448 if(next != null) 449 { 450 next.prev = prev; 451 } 452 453 head.prev = row; 454 row.next = head; 455 row.prev = null; 456 head = row; 457 } 458 } 459 460 private class CachedRow 461 { 462 public final Object pk; 463 public final Object [] fields; 464 public Object [] relations; 465 private Transaction locker; 466 467 private CachedRow next; 468 private CachedRow prev; 469 470 public long lastUpdated = System.currentTimeMillis(); 471 472 public CachedRow(Object pk, Object [] fields) 473 { 474 this.pk = pk; 475 this.fields = fields; 476 } 477 } 478 } 479 | Popular Tags |