PageRenderTime 39ms CodeModel.GetById 27ms app.highlight 10ms 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 */
 22package org.jboss.as.cmp.jdbc2.schema;
 23
 24import javax.transaction.Transaction;
 25import java.util.Map;
 26import java.util.HashMap;
 27import org.jboss.as.cmp.CmpMessages;
 28
 29
 30/**
 31 * Simple LRU cache. Items are evicted when maxCapacity is exceeded.
 32 *
 33 * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
 34 * @version <tt>$Revision: 89152 $</tt>
 35 * @jmx:mbean extends="org.jboss.system.ServiceMBean"
 36 */
 37public class TableCache implements Cache {
 38    private Cache.Listener listener = Cache.Listener.NOOP;
 39    private final Map rowsById;
 40    private CachedRow head;
 41    private CachedRow tail;
 42    private int maxCapacity;
 43    private final int minCapacity;
 44
 45    private boolean locked;
 46
 47    private final int partitionIndex;
 48
 49    public TableCache(int partitionIndex, int initialCapacity, int maxCapacity) {
 50        this.maxCapacity = maxCapacity;
 51        this.minCapacity = initialCapacity;
 52        rowsById = new HashMap(initialCapacity);
 53        this.partitionIndex = partitionIndex;
 54    }
 55
 56    /**
 57     * @jmx.managed-operation
 58     */
 59    public void registerListener(Cache.Listener listener) {
 60        this.listener = listener;
 61    }
 62
 63    /**
 64     * @jmx.managed-operation
 65     */
 66    public int size() {
 67        lock();
 68        try {
 69            return rowsById.size();
 70        } finally {
 71            unlock();
 72        }
 73    }
 74
 75    /**
 76     * @jmx.managed-attribute
 77     */
 78    public int getMaxCapacity() {
 79        return maxCapacity;
 80    }
 81
 82    /**
 83     * @jmx.managed-attribute
 84     */
 85    public void setMaxCapacity(int maxCapacity) {
 86        this.maxCapacity = maxCapacity;
 87    }
 88
 89    /**
 90     * @jmx.managed-attribute
 91     */
 92    public int getMinCapacity() {
 93        return minCapacity;
 94    }
 95
 96    public synchronized void lock() {
 97        boolean intr = false;
 98        try {
 99            if (locked) {
100                long start = System.currentTimeMillis();
101                while (locked) {
102                    try {
103                        wait();
104                    } catch (InterruptedException e) {
105                        intr = true;
106                    }
107                }
108
109                listener.contention(partitionIndex, System.currentTimeMillis() - start);
110            }
111            locked = true;
112        } finally {
113            if (intr) Thread.currentThread().interrupt();
114        }
115    }
116
117    public void lock(Object key) {
118        lock();
119    }
120
121    public synchronized void unlock() {
122        if (!locked) {
123            throw CmpMessages.MESSAGES.instanceIsLocked();
124        }
125        locked = false;
126        notify();
127    }
128
129    public void unlock(Object key) {
130        unlock();
131    }
132
133    public Object[] getFields(Object pk) {
134        Object[] fields;
135        CachedRow row = (CachedRow) rowsById.get(pk);
136        if (row != null && row.locker == null) {
137            promoteRow(row);
138            fields = new Object[row.fields.length];
139            System.arraycopy(row.fields, 0, fields, 0, fields.length);
140            listener.hit(partitionIndex);
141        } else {
142            fields = null;
143            listener.miss(partitionIndex);
144        }
145        return fields;
146    }
147
148    public Object[] getRelations(Object pk) {
149        Object[] relations;
150        CachedRow row = (CachedRow) rowsById.get(pk);
151        if (row != null && row.relations != null && row.locker == null) {
152            promoteRow(row);
153            relations = new Object[row.relations.length];
154            System.arraycopy(row.relations, 0, relations, 0, relations.length);
155        } else {
156            relations = null;
157        }
158        return relations;
159    }
160
161    public void put(Transaction tx, Object pk, Object[] fields, Object[] relations) {
162        CachedRow row = (CachedRow) rowsById.get(pk);
163        if (row == null) { // the row is not cached
164            Object[] fieldsCopy = new Object[fields.length];
165            System.arraycopy(fields, 0, fieldsCopy, 0, fields.length);
166            row = new CachedRow(pk, fieldsCopy);
167
168            if (relations != null) {
169                Object[] relationsCopy = new Object[relations.length];
170                System.arraycopy(relations, 0, relationsCopy, 0, relations.length);
171                row.relations = relationsCopy;
172            }
173
174            rowsById.put(pk, row);
175
176            if (head == null) {
177                head = row;
178                tail = row;
179            } else {
180                head.prev = row;
181                row.next = head;
182                head = row;
183            }
184        } else if (row.locker == null || row.locker.equals(tx)) { // the row is cached
185            promoteRow(row);
186            System.arraycopy(fields, 0, row.fields, 0, fields.length);
187
188            if (relations != null) {
189                if (row.relations == null) {
190                    row.relations = new Object[relations.length];
191                }
192                System.arraycopy(relations, 0, row.relations, 0, relations.length);
193            }
194
195            row.lastUpdated = System.currentTimeMillis();
196            row.locker = null;
197        }
198
199        CachedRow victim = tail;
200        while (rowsById.size() > maxCapacity && victim != null) {
201            CachedRow nextVictim = victim.prev;
202            if (victim.locker == null) {
203                dereference(victim);
204                rowsById.remove(victim.pk);
205                listener.eviction(partitionIndex, row.pk, rowsById.size());
206            }
207            victim = nextVictim;
208        }
209    }
210
211    public void ageOut(long lastUpdated) {
212        CachedRow victim = tail;
213        while (victim != null && victim.lastUpdated < lastUpdated) {
214            CachedRow nextVictim = victim.prev;
215            if (victim.locker == null) {
216                dereference(victim);
217                rowsById.remove(victim.pk);
218                listener.eviction(partitionIndex, victim.pk, rowsById.size());
219            }
220            victim = nextVictim;
221        }
222    }
223
224    public void remove(Transaction tx, Object pk) {
225        CachedRow row = (CachedRow) rowsById.remove(pk);
226        if (row == null || row.locker != null && !tx.equals(row.locker)) {
227            if(row == null) {
228                throw CmpMessages.MESSAGES.removeRejected(pk, tx);
229            } else {
230                throw CmpMessages.MESSAGES.removeRejected(pk, tx, row.locker);
231            }
232        }
233
234        dereference(row);
235        row.locker = null;
236    }
237
238    public boolean contains(Transaction tx, Object pk) {
239        CachedRow row = (CachedRow) rowsById.get(pk);
240        return row != null && (row.locker == null || tx.equals(row.locker));
241    }
242
243    public void lockForUpdate(Transaction tx, Object pk) throws Exception {
244        CachedRow row = (CachedRow) rowsById.get(pk);
245        if (row != null) {
246            if (row.locker != null && !tx.equals(row.locker)) {
247                throw CmpMessages.MESSAGES.lockAcquisitionRejected(tx, row.locker, pk);
248            }
249            row.locker = tx;
250        }
251        // else?!
252    }
253
254    public void releaseLock(Transaction tx, Object pk) throws Exception {
255        CachedRow row = (CachedRow) rowsById.get(pk);
256        if (row != null) {
257            if (!tx.equals(row.locker)) {
258                throw CmpMessages.MESSAGES.lockReleaseRejected(tx, row.locker, pk);
259            }
260            row.locker = null;
261        }
262        // else?!
263    }
264
265    public void flush() {
266        this.rowsById.clear();
267        this.head = null;
268        this.tail = null;
269    }
270
271    public String toString() {
272        StringBuffer buf = new StringBuffer();
273        buf.append('[');
274
275        try {
276            lock();
277
278            CachedRow cursor = head;
279            while (cursor != null) {
280                buf.append('(')
281                        .append(cursor.pk)
282                        .append('|');
283
284                for (int i = 0; i < cursor.fields.length; ++i) {
285                    if (i > 0) {
286                        buf.append(',');
287                    }
288
289                    buf.append(cursor.fields[i]);
290                }
291
292                buf.append(')');
293
294                cursor = cursor.next;
295            }
296        } finally {
297            unlock();
298        }
299
300        buf.append(']');
301        return buf.toString();
302    }
303
304    // Private
305
306    private void dereference(CachedRow row) {
307        CachedRow next = row.next;
308        CachedRow prev = row.prev;
309
310        if (row == head) {
311            head = next;
312        }
313
314        if (row == tail) {
315            tail = prev;
316        }
317
318        if (next != null) {
319            next.prev = prev;
320        }
321
322        if (prev != null) {
323            prev.next = next;
324        }
325
326        row.next = null;
327        row.prev = null;
328    }
329
330    private void promoteRow(CachedRow row) {
331        if (head == null) { // this is the first row in the cache
332            head = row;
333            tail = row;
334        } else if (row == head) { // this is the head
335        } else if (row == tail) { // this is the tail
336            tail = row.prev;
337            tail.next = null;
338
339            row.prev = null;
340            row.next = head;
341
342            head.prev = row;
343            head = row;
344        } else { // somewhere in the middle
345            CachedRow next = row.next;
346            CachedRow prev = row.prev;
347
348            if (prev != null) {
349                prev.next = next;
350            }
351
352            if (next != null) {
353                next.prev = prev;
354            }
355
356            head.prev = row;
357            row.next = head;
358            row.prev = null;
359            head = row;
360        }
361    }
362
363    private class CachedRow {
364        public final Object pk;
365        public final Object[] fields;
366        public Object[] relations;
367        private Transaction locker;
368
369        private CachedRow next;
370        private CachedRow prev;
371
372        public long lastUpdated = System.currentTimeMillis();
373
374        public CachedRow(Object pk, Object[] fields) {
375            this.pk = pk;
376            this.fields = fields;
377        }
378    }
379}