PageRenderTime 34ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/jboss-as-7.1.1.Final/cmp/src/main/java/org/jboss/as/cmp/jdbc2/schema/TableCache.java

#
Java | 379 lines | 276 code | 57 blank | 46 comment | 86 complexity | c6a5aadb847537f262b6254c01f11aa8 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  1. /*
  2. * JBoss, Home of Professional Open Source.
  3. * Copyright 2008, Red Hat Middleware LLC, and individual contributors
  4. * as indicated by the @author tags. See the copyright.txt file in the
  5. * distribution for a full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.jboss.as.cmp.jdbc2.schema;
  23. import javax.transaction.Transaction;
  24. import java.util.Map;
  25. import java.util.HashMap;
  26. import org.jboss.as.cmp.CmpMessages;
  27. /**
  28. * Simple LRU cache. Items are evicted when maxCapacity is exceeded.
  29. *
  30. * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
  31. * @version <tt>$Revision: 89152 $</tt>
  32. * @jmx:mbean extends="org.jboss.system.ServiceMBean"
  33. */
  34. public class TableCache implements Cache {
  35. private Cache.Listener listener = Cache.Listener.NOOP;
  36. private final Map rowsById;
  37. private CachedRow head;
  38. private CachedRow tail;
  39. private int maxCapacity;
  40. private final int minCapacity;
  41. private boolean locked;
  42. private final int partitionIndex;
  43. public TableCache(int partitionIndex, int initialCapacity, int maxCapacity) {
  44. this.maxCapacity = maxCapacity;
  45. this.minCapacity = initialCapacity;
  46. rowsById = new HashMap(initialCapacity);
  47. this.partitionIndex = partitionIndex;
  48. }
  49. /**
  50. * @jmx.managed-operation
  51. */
  52. public void registerListener(Cache.Listener listener) {
  53. this.listener = listener;
  54. }
  55. /**
  56. * @jmx.managed-operation
  57. */
  58. public int size() {
  59. lock();
  60. try {
  61. return rowsById.size();
  62. } finally {
  63. unlock();
  64. }
  65. }
  66. /**
  67. * @jmx.managed-attribute
  68. */
  69. public int getMaxCapacity() {
  70. return maxCapacity;
  71. }
  72. /**
  73. * @jmx.managed-attribute
  74. */
  75. public void setMaxCapacity(int maxCapacity) {
  76. this.maxCapacity = maxCapacity;
  77. }
  78. /**
  79. * @jmx.managed-attribute
  80. */
  81. public int getMinCapacity() {
  82. return minCapacity;
  83. }
  84. public synchronized void lock() {
  85. boolean intr = false;
  86. try {
  87. if (locked) {
  88. long start = System.currentTimeMillis();
  89. while (locked) {
  90. try {
  91. wait();
  92. } catch (InterruptedException e) {
  93. intr = true;
  94. }
  95. }
  96. listener.contention(partitionIndex, System.currentTimeMillis() - start);
  97. }
  98. locked = true;
  99. } finally {
  100. if (intr) Thread.currentThread().interrupt();
  101. }
  102. }
  103. public void lock(Object key) {
  104. lock();
  105. }
  106. public synchronized void unlock() {
  107. if (!locked) {
  108. throw CmpMessages.MESSAGES.instanceIsLocked();
  109. }
  110. locked = false;
  111. notify();
  112. }
  113. public void unlock(Object key) {
  114. unlock();
  115. }
  116. public Object[] getFields(Object pk) {
  117. Object[] fields;
  118. CachedRow row = (CachedRow) rowsById.get(pk);
  119. if (row != null && row.locker == null) {
  120. promoteRow(row);
  121. fields = new Object[row.fields.length];
  122. System.arraycopy(row.fields, 0, fields, 0, fields.length);
  123. listener.hit(partitionIndex);
  124. } else {
  125. fields = null;
  126. listener.miss(partitionIndex);
  127. }
  128. return fields;
  129. }
  130. public Object[] getRelations(Object pk) {
  131. Object[] relations;
  132. CachedRow row = (CachedRow) rowsById.get(pk);
  133. if (row != null && row.relations != null && row.locker == null) {
  134. promoteRow(row);
  135. relations = new Object[row.relations.length];
  136. System.arraycopy(row.relations, 0, relations, 0, relations.length);
  137. } else {
  138. relations = null;
  139. }
  140. return relations;
  141. }
  142. public void put(Transaction tx, Object pk, Object[] fields, Object[] relations) {
  143. CachedRow row = (CachedRow) rowsById.get(pk);
  144. if (row == null) { // the row is not cached
  145. Object[] fieldsCopy = new Object[fields.length];
  146. System.arraycopy(fields, 0, fieldsCopy, 0, fields.length);
  147. row = new CachedRow(pk, fieldsCopy);
  148. if (relations != null) {
  149. Object[] relationsCopy = new Object[relations.length];
  150. System.arraycopy(relations, 0, relationsCopy, 0, relations.length);
  151. row.relations = relationsCopy;
  152. }
  153. rowsById.put(pk, row);
  154. if (head == null) {
  155. head = row;
  156. tail = row;
  157. } else {
  158. head.prev = row;
  159. row.next = head;
  160. head = row;
  161. }
  162. } else if (row.locker == null || row.locker.equals(tx)) { // the row is cached
  163. promoteRow(row);
  164. System.arraycopy(fields, 0, row.fields, 0, fields.length);
  165. if (relations != null) {
  166. if (row.relations == null) {
  167. row.relations = new Object[relations.length];
  168. }
  169. System.arraycopy(relations, 0, row.relations, 0, relations.length);
  170. }
  171. row.lastUpdated = System.currentTimeMillis();
  172. row.locker = null;
  173. }
  174. CachedRow victim = tail;
  175. while (rowsById.size() > maxCapacity && victim != null) {
  176. CachedRow nextVictim = victim.prev;
  177. if (victim.locker == null) {
  178. dereference(victim);
  179. rowsById.remove(victim.pk);
  180. listener.eviction(partitionIndex, row.pk, rowsById.size());
  181. }
  182. victim = nextVictim;
  183. }
  184. }
  185. public void ageOut(long lastUpdated) {
  186. CachedRow victim = tail;
  187. while (victim != null && victim.lastUpdated < lastUpdated) {
  188. CachedRow nextVictim = victim.prev;
  189. if (victim.locker == null) {
  190. dereference(victim);
  191. rowsById.remove(victim.pk);
  192. listener.eviction(partitionIndex, victim.pk, rowsById.size());
  193. }
  194. victim = nextVictim;
  195. }
  196. }
  197. public void remove(Transaction tx, Object pk) {
  198. CachedRow row = (CachedRow) rowsById.remove(pk);
  199. if (row == null || row.locker != null && !tx.equals(row.locker)) {
  200. if(row == null) {
  201. throw CmpMessages.MESSAGES.removeRejected(pk, tx);
  202. } else {
  203. throw CmpMessages.MESSAGES.removeRejected(pk, tx, row.locker);
  204. }
  205. }
  206. dereference(row);
  207. row.locker = null;
  208. }
  209. public boolean contains(Transaction tx, Object pk) {
  210. CachedRow row = (CachedRow) rowsById.get(pk);
  211. return row != null && (row.locker == null || tx.equals(row.locker));
  212. }
  213. public void lockForUpdate(Transaction tx, Object pk) throws Exception {
  214. CachedRow row = (CachedRow) rowsById.get(pk);
  215. if (row != null) {
  216. if (row.locker != null && !tx.equals(row.locker)) {
  217. throw CmpMessages.MESSAGES.lockAcquisitionRejected(tx, row.locker, pk);
  218. }
  219. row.locker = tx;
  220. }
  221. // else?!
  222. }
  223. public void releaseLock(Transaction tx, Object pk) throws Exception {
  224. CachedRow row = (CachedRow) rowsById.get(pk);
  225. if (row != null) {
  226. if (!tx.equals(row.locker)) {
  227. throw CmpMessages.MESSAGES.lockReleaseRejected(tx, row.locker, pk);
  228. }
  229. row.locker = null;
  230. }
  231. // else?!
  232. }
  233. public void flush() {
  234. this.rowsById.clear();
  235. this.head = null;
  236. this.tail = null;
  237. }
  238. public String toString() {
  239. StringBuffer buf = new StringBuffer();
  240. buf.append('[');
  241. try {
  242. lock();
  243. CachedRow cursor = head;
  244. while (cursor != null) {
  245. buf.append('(')
  246. .append(cursor.pk)
  247. .append('|');
  248. for (int i = 0; i < cursor.fields.length; ++i) {
  249. if (i > 0) {
  250. buf.append(',');
  251. }
  252. buf.append(cursor.fields[i]);
  253. }
  254. buf.append(')');
  255. cursor = cursor.next;
  256. }
  257. } finally {
  258. unlock();
  259. }
  260. buf.append(']');
  261. return buf.toString();
  262. }
  263. // Private
  264. private void dereference(CachedRow row) {
  265. CachedRow next = row.next;
  266. CachedRow prev = row.prev;
  267. if (row == head) {
  268. head = next;
  269. }
  270. if (row == tail) {
  271. tail = prev;
  272. }
  273. if (next != null) {
  274. next.prev = prev;
  275. }
  276. if (prev != null) {
  277. prev.next = next;
  278. }
  279. row.next = null;
  280. row.prev = null;
  281. }
  282. private void promoteRow(CachedRow row) {
  283. if (head == null) { // this is the first row in the cache
  284. head = row;
  285. tail = row;
  286. } else if (row == head) { // this is the head
  287. } else if (row == tail) { // this is the tail
  288. tail = row.prev;
  289. tail.next = null;
  290. row.prev = null;
  291. row.next = head;
  292. head.prev = row;
  293. head = row;
  294. } else { // somewhere in the middle
  295. CachedRow next = row.next;
  296. CachedRow prev = row.prev;
  297. if (prev != null) {
  298. prev.next = next;
  299. }
  300. if (next != null) {
  301. next.prev = prev;
  302. }
  303. head.prev = row;
  304. row.next = head;
  305. row.prev = null;
  306. head = row;
  307. }
  308. }
  309. private class CachedRow {
  310. public final Object pk;
  311. public final Object[] fields;
  312. public Object[] relations;
  313. private Transaction locker;
  314. private CachedRow next;
  315. private CachedRow prev;
  316. public long lastUpdated = System.currentTimeMillis();
  317. public CachedRow(Object pk, Object[] fields) {
  318. this.pk = pk;
  319. this.fields = fields;
  320. }
  321. }
  322. }