001: /*
002: * Copyright 2003 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package velosurf.cache;
018:
019: import java.lang.ref.SoftReference;
020: import java.util.HashMap;
021: import java.util.Map;
022:
023: /** <p>Cache that keeps fetched instances in memory.</p>
024: *
025: * <p>Three modes (defined for <<code>database</code>> or for <<code>entity</code>> in <code>model.xml</code>:</p>
026: * <ul>
027: * <li>NO_CACHE (cache='none', the default) : no caching occurs on this entity.
028: * <li>SOFT_CACHE (cache='soft') : caching occurs as long as memory is ont reclaimed (see the behaviour of java soft references).
029: * <li>FULL_CACHE (cache='full') : the whole table is loaded into the cache at startup.
030: * </ul>
031: *
032: * <p>For an entity's instances to be cached, the associated table must have a primary key (even if multivalued).</p>
033: *
034: * <p><b>Warning</b>: Velosurf will invalidate entries on single row update and delete queries, but global updates and deletes are not taken into account.</p>
035: * <p>This caching mechanism is meant for straightforward optimizations in simple situations, for instance to
036: * avoid re-fetching the loggued user at each request.</p>
037: *
038: * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
039: *
040: */
041: public class Cache {
042:
043: /** Constant used to specify the "no cache" mode.
044: */
045: public static final int NO_CACHE = 0;
046: /** Constant used to specify the "soft cache" mode.
047: */
048: public static final int SOFT_CACHE = 1;
049: /** Constant used to specify the "full cache" mode.
050: */
051: public static final int FULL_CACHE = 2;
052:
053: /** Cache constructor.
054: *
055: * @param cachingMethod required caching mode
056: */
057: public Cache(int cachingMethod) {
058: this .cachingMethod = cachingMethod;
059: innerCache = new HashMap();
060: }
061:
062: /** Put an instance in the cache.
063: *
064: * @param key key field(s) of this instance
065: * @param value instance
066: */
067: public void put(Object key, Object value) {
068: key = (key.getClass().isArray() ? new ArrayKey((Object[]) key)
069: : key);
070: value = (cachingMethod == SOFT_CACHE ? new SoftReference<Object>(
071: value)
072: : value);
073: synchronized (innerCache) {
074: innerCache.put(key, value);
075: }
076: }
077:
078: /** Getter for the size of the cache.
079: *
080: * @return the size of the cache
081: */
082: public int size() {
083: return innerCache.size();
084: }
085:
086: /** Try to get an instance from the cache.
087: *
088: * @param key key field(s) of the asked instance
089: * @return Asked instance or null if not found
090: */
091: public Object get(Object key) {
092: key = (key.getClass().isArray() ? new ArrayKey((Object[]) key)
093: : key);
094: Object ret;
095: synchronized (innerCache) {
096: ret = innerCache.get(key);
097: }
098: if (ret != null && cachingMethod == SOFT_CACHE) {
099: ret = ((SoftReference<Object>) ret).get();
100: /* if null, clean cache */
101: if (ret == null) {
102: synchronized (innerCache) {
103: innerCache.remove(key);
104: }
105: }
106: }
107: return ret;
108: }
109:
110: /** Clear the cache.
111: *
112: */
113: public void clear() {
114: innerCache.clear();
115: }
116:
117: /** invalidates an entry
118: * (used after an insert or an update)
119: */
120: public void invalidate(Object key) {
121: synchronized (innerCache) {
122: innerCache.remove(key);
123: }
124:
125: }
126:
127: /** The caching method this cache uses.
128: */
129: private int cachingMethod;
130: /** the inner map that stores associations.
131: */
132: private Map<Object, Object> innerCache = null;
133:
134: /**
135: * ArrayKey is a simple wrapper that provides a field-to-field equal method between encapsulated arrays.
136: */
137: public static final class ArrayKey {
138:
139: /**
140: * Constructor.
141: * @param keys key values
142: */
143: public ArrayKey(Object[] keys) {
144: this .keys = keys;
145: }
146:
147: /** Checks the cell-to-cell equality of two arrays.
148: *
149: * @param source source array
150: * @return a boolean indicating the equality
151: */
152: public boolean equals(Object source) {
153: if (source instanceof ArrayKey) {
154: ArrayKey k = (ArrayKey) source;
155: if (k.keys.length == keys.length) {
156: for (int i = 0; i < keys.length; i++)
157: if (!keys[i].equals(k.keys[i]))
158: return false;
159: return true;
160: }
161: }
162: return false;
163: }
164:
165: /** Hashcode of an array, based on the hashcode of its members.
166: *
167: * @return the hashcode
168: */
169: public int hashCode() {
170: int hash = 0;
171: for (int i = 0; i < keys.length; i++)
172: hash += keys[i].hashCode();
173: return hash;
174: }
175:
176: /** The wrapped array.
177: */
178: private Object[] keys = null;
179: } /* end of inner class ArrayKey */
180: }
|