001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.server;
012:
013: import com.versant.core.jdo.QueryDetails;
014:
015: import java.util.Map;
016: import java.util.HashMap;
017:
018: /**
019: * LRU map of QueryDetails -> CompiledQuery. Also assigns a unique ID to
020: * each query added to the cache and supports lookup by this ID.
021: */
022: public final class CompiledQueryCache {
023:
024: private final int maxSize;
025: private final Map map = new HashMap(); // QueryDetails -> Entry
026: private final Map idMap = new HashMap(); // Integer ID -> Entry
027: private Entry head, tail;
028: private int lastID;
029:
030: private class Entry {
031: CompiledQuery cq;
032: Entry prev, next;
033: }
034:
035: public CompiledQueryCache(int maxSize) {
036: this .maxSize = maxSize <= 0 ? 1000 : maxSize;
037: }
038:
039: /**
040: * Get the cached CompiledQuery for q or null if none. This will make it
041: * the most recently used query.
042: */
043: public synchronized CompiledQuery get(QueryDetails q) {
044: Entry e = (Entry) map.get(q);
045: if (e == null) {
046: return null;
047: }
048: removeFromLRUList(e);
049: addToHeadOfLRUList(e);
050: return e.cq;
051: }
052:
053: /**
054: * Get the cached CompiledQuery for id or null if none. This will make it
055: * the most recently used query.
056: */
057: public synchronized CompiledQuery get(int id) {
058: Entry e = (Entry) idMap.get(new Integer(id));
059: if (e == null) {
060: return null;
061: }
062: removeFromLRUList(e);
063: addToHeadOfLRUList(e);
064: return e.cq;
065: }
066:
067: /**
068: * Add cq to the cache. If there is already an entry for cq in the cache
069: * then the current entry is returned and nothing is done. This avoids the
070: * need to synchronize on this cache during query compilation. It is
071: * possible that two threads might compile the same query after discovering
072: * that it is not in cache but this is ok.
073: */
074: public synchronized CompiledQuery add(CompiledQuery cq) {
075: Entry e = (Entry) map.get(cq.getQueryDetails());
076: if (e != null) {
077: return e.cq;
078: }
079: e = new Entry();
080: e.cq = cq;
081: int id = ++lastID;
082: cq.setId(id);
083: map.put(cq.getQueryDetails(), e);
084: idMap.put(new Integer(id), e);
085: addToHeadOfLRUList(e);
086: for (int c = map.size() - maxSize; c > 0; c--) {
087: map.remove(tail.cq.getQueryDetails());
088: idMap.remove(new Integer(tail.cq.getId()));
089: tail.next.prev = null;
090: tail = tail.next;
091: }
092: return cq;
093: }
094:
095: /**
096: * Empty the cache.
097: */
098: public synchronized void clear() {
099: map.clear();
100: head = tail = null;
101: }
102:
103: /**
104: * Remove e from the double linked LRU list.
105: */
106: private void removeFromLRUList(Entry e) {
107: if (e.prev != null) {
108: e.prev.next = e.next;
109: } else {
110: tail = e.next;
111: }
112: if (e.next != null) {
113: e.next.prev = e.prev;
114: } else {
115: head = e.prev;
116: }
117: e.next = e.prev = null;
118: }
119:
120: /**
121: * Add e to the head of the double linked LRU list. This will make it the
122: * most recently accessed object.
123: */
124: private void addToHeadOfLRUList(Entry e) {
125: e.next = null;
126: e.prev = head;
127: if (head != null) {
128: head.next = e;
129: }
130: head = e;
131: if (tail == null) {
132: tail = e;
133: }
134: }
135:
136: }
|