001: package com.jofti.cache.adapter;
002:
003: import java.util.Iterator;
004: import java.util.Map;
005: import java.util.Properties;
006: import java.util.Set;
007:
008: import org.apache.commons.logging.Log;
009: import org.apache.commons.logging.LogFactory;
010:
011: import com.jofti.api.IndexQuery;
012: import com.jofti.cache.CacheAdapter;
013: import com.jofti.cache.BaseAdaptor;
014: import com.jofti.core.INameSpaceAware;
015: import com.jofti.core.IParsedQuery;
016: import com.jofti.core.InternalIndex;
017: import com.jofti.core.QueryId;
018: import com.jofti.core.QueryType;
019: import com.jofti.exception.JoftiException;
020: import com.jofti.util.CompositeComparator;
021:
022: import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
023:
024: /**
025:
026: *
027: * The adapter implementation specific to the Map interface.
028: *
029: * The adapter takes care of the implementation specific details for converting
030: * between the process the index expects and the behaviour of the Map.
031: *
032: * Although it is possible to use this adapter with any Map implementation, it
033: * does not deal with the LinkedHashMap where it has been configured to run as
034: * an LRU cache. As values will dispear from the Map without callback to the index. This also applies to other Map
035: * type structures that silently expire elements.<p>
036: *
037: * Care must be taken when using Maps with similar expiry functionality as it <b> will</b> lead to memory leaks.
038: *
039: *
040: * The start up of the adapter will also try and index any elements already in
041: * the Map.<p>
042: *
043: * @author Steve Woodcock
044: * <p>
045: * @version 1.0
046: * <p>
047: */
048:
049: public class MapAdapter extends BaseAdaptor implements CacheAdapter {
050:
051: private static Log log = LogFactory.getLog(MapAdapter.class);
052:
053: private Map map = new ConcurrentHashMap();
054:
055: private String name;
056:
057: public MapAdapter() {
058: }
059:
060: public MapAdapter(Object cache) {
061:
062: this .map = (Map) cache;
063: }
064:
065: public void setCacheImpl(Object cache) {
066: this .map = (Map) cache;
067:
068: }
069:
070: /* (non-Javadoc)
071: * @see com.jofti.api.IndexCache#get(java.lang.Object)
072: */
073: public Object get(Object key) {
074:
075: Object result = null;
076: if (key != null) {
077:
078: result = map.get(key);
079:
080: }
081: return result;
082:
083: }
084:
085: /*
086: * (non-Javadoc)
087: *
088: * @see com.jofti.api.IndexCache#put(java.lang.Object, java.lang.Object)
089: */
090: public void put(Object key, Object value) throws JoftiException {
091:
092: Comparable newKey = (Comparable) decorateKey(key);
093:
094: acquireUpdateLock();
095: try {
096: synchronized (getCacheLock(key)) {
097: if (index.contains(newKey)) {
098: index.removeByKey(newKey);
099: }
100:
101: // put the value back in
102: map.put(key, value);
103: index.insert(newKey, value);
104:
105: }
106: } finally {
107: releaseUpdateLock();
108: }
109: }
110:
111: /**
112: * Removes the element which matches the key.
113: * <p>
114: * If no element matches, nothing is removed and no Exception is thrown.
115: * @param key the key of the element to remove
116: * @throws CacheException
117: */
118: public void remove(Object key) throws JoftiException {
119: Comparable newKey = (Comparable) decorateKey(key);
120:
121: acquireUpdateLock();
122: try {
123: synchronized (getCacheLock(key)) {
124: map.remove(key);
125:
126: if (index.contains(newKey)) {
127: index.removeByKey(newKey);
128: }
129:
130: }
131:
132: } catch (Exception e) {
133: throw new JoftiException(e);
134: } finally {
135: releaseUpdateLock();
136: }
137:
138: }
139:
140: /**
141: * Remove all elements in the cache, but leave the cache in a useable state.
142: *
143: * @throws CacheException
144: */
145: public void removeAll() throws JoftiException {
146: acquireUpdateLock();
147: try {
148: if (map != null) {
149: map.clear();
150: }
151: if (index != null) {
152: index.removeAll();
153: }
154: } catch (Exception e) {
155: throw new JoftiException(e);
156: } finally {
157: releaseUpdateLock();
158: }
159:
160: }
161:
162: /* (non-Javadoc)
163: * @see com.jofti.cache.LifeCycleAdapter#init(java.util.Properties)
164: */
165: public synchronized void init(Properties properties)
166: throws JoftiException {
167:
168: if (properties != null) {
169: String key = null;
170: for (Iterator it = properties.keySet().iterator(); it
171: .hasNext();) {
172: key = (String) it.next();
173: if (MUTABLE_VALUES.equalsIgnoreCase(key)) {
174: checkMutable = Boolean.valueOf(
175: properties.getProperty(key)).booleanValue();
176: if (log.isInfoEnabled()) {
177: log.info("Mutability checking is set to "
178: + checkMutable);
179: }
180: }
181:
182: }
183: }
184: }
185:
186: /* (non-Javadoc)
187: * @see com.jofti.cache.LifeCycleAdapter#destroy()
188: */
189: public void destroy() throws JoftiException {
190:
191: map = new ConcurrentHashMap();
192:
193: if (index != null) {
194: index.removeAll();
195: }
196: }
197:
198: /* (non-Javadoc)
199: * @see com.jofti.cache.LifeCycleAdapter#getName()
200: */
201: public String getName() {
202: return name;
203: }
204:
205: /* (non-Javadoc)
206: * @see com.jofti.cache.LifeCycleAdapter#setName(java.lang.String)
207: */
208: public void setName(String name) {
209: this .name = name;
210: }
211:
212: public String toString() {
213: return "HashMapCache(" + getName() + ')';
214: }
215:
216: /* (non-Javadoc)
217: * @see com.jofti.api.IndexCache#getCacheImpl()
218: */
219: public Object getCacheImpl() {
220:
221: return map;
222: }
223:
224: /* (non-Javadoc)
225: * @see com.jofti.cache.CacheAdapter#setInternalIndex(com.jofti.core.InternalIndex)
226: */
227: public void setInternalIndex(InternalIndex index) {
228: this .index = index;
229:
230: }
231:
232: /* (non-Javadoc)
233: * @see com.jofti.cache.CacheAdapter#start()
234: */
235: public void start() throws JoftiException {
236:
237: loadInitialValues(map);
238:
239: }
240:
241: private void loadInitialValues(Map temp) throws JoftiException {
242: if (map == null) {
243: log.info("No initial values to index as map is null");
244: return;
245: }
246: Set entries = temp.entrySet();
247: int size = entries.size();
248: Iterator it = entries.iterator();
249: for (int i = 0; i < size; i++) {
250:
251: Map.Entry entry = (Map.Entry) it.next();
252:
253: if (entry.getKey() instanceof Comparable) {
254: if (entry.getValue() != null) {
255: // then we must remove from index
256: index.insert((Comparable) entry.getKey(), entry
257: .getValue());
258: }
259: } else {
260: log.warn("unable to index value at " + entry.getKey()
261: + "as key is not Comparable");
262: }
263: }
264: }
265:
266: /* (non-Javadoc)
267: * @see com.jofti.cache.CacheLocking#getCacheValue(java.lang.Object)
268: */
269: protected Object getCacheValue(Object key) {
270: synchronized (getCacheLock(key)) {
271: return map.get(key);
272: }
273: }
274:
275: /* (non-Javadoc)
276: * @see com.jofti.cache.CacheLocking#getIndex()
277: */
278: public InternalIndex getIndex() {
279: return index;
280: }
281:
282: public IndexQuery addQuery(String name, IndexQuery query)
283: throws JoftiException {
284:
285: return index.getParserManager().addQuery(name, query);
286: }
287:
288: /* (non-Javadoc)
289: * @see com.jofti.api.Index#getQuery(java.lang.String)
290: */
291: public IndexQuery getQuery(String name) {
292:
293: return index.getParserManager().getQuery(name);
294: }
295:
296: }
|