001: package com.jofti.cache.adapter;
002:
003: import java.io.IOException;
004: import java.io.Serializable;
005: import java.util.Iterator;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Properties;
009:
010: import net.sf.ehcache.CacheException;
011: import net.sf.ehcache.Element;
012:
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: import com.jofti.api.IndexQuery;
017: import com.jofti.cache.CacheAdapter;
018: import com.jofti.cache.BaseAdaptor;
019: import com.jofti.core.INameSpaceAware;
020: import com.jofti.core.IParsedQuery;
021: import com.jofti.core.InternalIndex;
022: import com.jofti.core.QueryId;
023: import com.jofti.core.QueryType;
024: import com.jofti.exception.JoftiException;
025: import com.jofti.util.CompositeComparator;
026:
027: /**
028:
029: *
030: * The adapter implementation specific to EHCache.
031: *
032: * The adapter takes care of the implementation specific details for converting
033: * between the process the index expects and the behaviour of EHCache (pre-1.2 versions).
034: *
035: * The main drawback with ealier versions of EHCache is that it is not possible to find out when an
036: * entry has been expired in the cache so there is a possibility that if you do
037: * not access the cache entries the index will end up with extra keys that are
038: * not present in the cache.
039: *
040: * While not a huge problem, over time this could be an issue in an ongoing
041: * situation. It is strongly recommended that you upgrade to version 1.2 and use the Listener or
042: * wrapper adapter.</p>
043: *
044: * The start up of the adapter will also try and index any elements already in
045: * the cache.<P>
046: *
047: * @author Steve Woodcock
048: * <p>
049: * @version 1.0
050: * <p>
051: */
052: public class EhCachePre1_2Adapter extends BaseAdaptor implements
053: CacheAdapter {
054:
055: private net.sf.ehcache.CacheManager manager;
056:
057: private static Log log = LogFactory
058: .getLog(EhCachePre1_2Adapter.class);
059:
060: private net.sf.ehcache.Cache cache;
061:
062: private String name;
063:
064: public EhCachePre1_2Adapter() {
065: }
066:
067: public EhCachePre1_2Adapter(Object cache) {
068: this .cache = (net.sf.ehcache.Cache) cache;
069:
070: }
071:
072: public void setCacheImpl(Object cache) {
073: this .cache = (net.sf.ehcache.Cache) cache;
074:
075: }
076:
077: /*
078: * (non-Javadoc)
079: *
080: * @see com.jofti.api.IndexCache#get(java.lang.Object)
081: */
082: public Object get(Object key) {
083: key = decorateKey(key);
084: try {
085: if (key != null) {
086: // do the cast early - seems to help the performance slightly -
087: // otherwise
088: // we have multiple casts in this method
089: Comparable tempKey = (Comparable) key;
090: // get a lock for the key that we are trying to add
091: // we do not care about other parts of the cache or the index
092: synchronized (getCacheLock(key)) {
093:
094: // get the element from the cache if it exists
095: Element element = cache.get((Serializable) tempKey);
096: // if it is null either it has expired or it was never there
097: if (element == null) {
098:
099: // we always do this as we can't tell if it has expired
100: // unless we have the full
101: // object - which we can't get because it has expired!
102: // this is crap - but there is little other option
103: // have raised a change request in ehcache
104: if (log.isDebugEnabled()) {
105: log
106: .debug("unexpected null object retrieved for key "
107: + key + " removing");
108: }
109: // check if it exists first (might always have been
110: // null) as otherwise we have to remove anyway
111:
112: if (index.contains(tempKey)) {
113: index.removeByKey(tempKey);
114: }
115:
116: return null;
117: } else {
118: // we should not need to do this as there should be no
119: // way an entry can just appear without going through
120: // this classe
121:
122: return element.getValue();
123: }
124: }
125:
126: } else {
127: return null;
128: }
129: } catch (net.sf.ehcache.CacheException e) {
130: log.warn("Unable to retrieve value from cache", e);
131:
132: } catch (JoftiException e) {
133: log.warn("Unable to retrieve value from cache", e);
134: }
135: return null;
136: }
137:
138: /*
139: * (non-Javadoc)
140: *
141: * @see com.jofti.api.IndexCache#put(java.lang.Object, java.lang.Object)
142: */
143: public void put(Object key, Object value) throws JoftiException {
144: key = decorateKey(key);
145: acquireUpdateLock();
146: try {
147: Comparable tempKey = (Comparable) key;
148:
149: synchronized (getCacheLock(key)) {
150:
151: // we have to remove a potentially expired result
152: if (index.contains(tempKey)) {
153: if (log.isDebugEnabled()) {
154: log.debug("Object does exist so remove "
155: + tempKey);
156: }
157:
158: index.removeByKey(tempKey);
159: }
160:
161: Element element = new Element((Serializable) tempKey,
162: (Serializable) value);
163: cache.put(element);
164:
165: index.insert(tempKey, value);
166:
167: }
168: } catch (IllegalArgumentException e) {
169: throw new JoftiException(e);
170: } catch (IllegalStateException e) {
171: throw new JoftiException(e);
172: } finally {
173: releaseUpdateLock();
174: }
175:
176: }
177:
178: /**
179: * Removes the element which matches the key.
180: * <p>
181: * If no element matches, nothing is removed and no Exception is thrown.
182: *
183: * @param key
184: * the key of the element to remove
185: * @throws CacheException
186: */
187: public void remove(Object key) throws JoftiException {
188: key = decorateKey(key);
189:
190: acquireUpdateLock();
191: try {
192:
193: synchronized (getCacheLock(key)) {
194: Comparable tempKey = (Comparable) key;
195: // remove if we need to based on the key - as it may be expired
196: // this synchronous as we have to remove these entries before
197: // we can return
198: if (index.contains(tempKey)) {
199: index.removeByKey(tempKey);
200: }
201:
202: cache.remove((Serializable) key);
203: }
204:
205: } catch (ClassCastException e) {
206: throw new JoftiException(e);
207: } catch (IllegalStateException e) {
208: throw new JoftiException(e);
209: } finally {
210: releaseUpdateLock();
211: }
212: }
213:
214: /**
215: * Remove all elements in the cache, but leave the cache in a useable state.
216: *
217: * @throws CacheException
218: */
219: public synchronized void removeAll() throws JoftiException {
220: try {
221:
222: cache.removeAll();
223: index.removeAll();
224:
225: } catch (IllegalStateException e) {
226: throw new JoftiException(e);
227: } catch (Exception e) {
228: throw new JoftiException(e);
229: }
230: }
231:
232: public synchronized void init(Properties properties)
233: throws JoftiException {
234: try {
235: String cacheConfigFile = null;
236: if (properties != null) {
237: String key = null;
238: for (Iterator it = properties.keySet().iterator(); it
239: .hasNext();) {
240: key = (String) it.next();
241: if (MUTABLE_VALUES.equalsIgnoreCase(key)) {
242: checkMutable = Boolean.valueOf(
243: properties.getProperty(key))
244: .booleanValue();
245: if (log.isInfoEnabled()) {
246: log.info("Mutability checking is set to "
247: + checkMutable);
248: }
249: }
250: if ("file".equalsIgnoreCase(key)) {
251: cacheConfigFile = properties.getProperty(key);
252: }
253: }
254: }
255:
256: if (manager == null) {
257: if (cacheConfigFile != null) {
258: manager = net.sf.ehcache.CacheManager
259: .create(cacheConfigFile);
260: } else {
261: manager = net.sf.ehcache.CacheManager.create();
262: }
263: }
264: if (cache == null) {
265: initCache();
266: } else {
267: // try and add it to the cache
268: if (!(manager.cacheExists(cache.getName()))) {
269: manager.addCache(cache);
270: } else {
271: log
272: .warn("IndexCache "
273: + cache.getName()
274: + "already exists in manager - not adding new cache");
275: }
276: }
277:
278: } catch (net.sf.ehcache.CacheException e) {
279: throw new JoftiException(e);
280: }
281:
282: }
283:
284: private void initCache() throws JoftiException {
285: if (cache == null) {
286: cache = manager.getCache(name);
287: if (cache == null) {
288: log.warn("Unable to find cache named '" + name
289: + "' in EHCacheManager");
290: String[] names = manager.getCacheNames();
291: log.warn("Available cache names are:");
292: for (int i = 0; i < names.length; i++) {
293: log.warn("IndexCache:" + names[i]);
294: }
295: throw new JoftiException("Unable to find cache named '"
296: + name + "' in EHCacheManager");
297: }
298: }
299: }
300:
301: /* (non-Javadoc)
302: * @see com.jofti.cache.LifeCycleAdapter#start()
303: */
304: public synchronized void start() throws JoftiException {
305: try {
306: loadInitialValues(cache);
307: } catch (CacheException ce) {
308: throw new JoftiException(ce);
309: }
310: }
311:
312: private void loadInitialValues(net.sf.ehcache.Cache cache)
313: throws CacheException, JoftiException {
314: List keys = cache.getKeys();
315: for (Iterator it = keys.iterator(); it.hasNext();) {
316: Object key = it.next();
317: Element element = cache.get((Serializable) key);
318: if (key instanceof Comparable) {
319: Comparable tempKey = (Comparable) key;
320: if (element != null && element.getValue() != null) {
321: // then we must remove from index
322: index.insert(tempKey, element.getValue());
323: }
324: } else {
325: log.info("Ignoring value at key:" + key
326: + " as key is not Comparable");
327: }
328: }
329: }
330:
331: /* (non-Javadoc)
332: * @see com.jofti.cache.LifeCycleAdapter#destroy()
333: */
334: public synchronized void destroy() throws JoftiException {
335: try {
336: if (manager != null) {
337: manager.removeCache(name);
338: manager.shutdown();
339: index.removeAll();
340: }
341: } catch (IllegalStateException e) {
342: throw new JoftiException(e);
343: }
344: }
345:
346: public String getName() {
347: return name;
348: }
349:
350: public void setName(String name) {
351: this .name = name;
352: }
353:
354: public String toString() {
355: return "EHCache(" + getName() + ')';
356: }
357:
358: public Object getCache() {
359: return cache;
360: }
361:
362: /*
363: * (non-Javadoc)
364: *
365: * @see com.jofti.api.IndexCache#getCacheImpl()
366: */
367: public Object getCacheImpl() {
368: return cache;
369: }
370:
371: /*
372: * (non-Javadoc)
373: *
374: * @see com.jofticache.CacheAdapter#setInternalIndex(com.jofti.core.InternalIndex)
375: */
376: public void setInternalIndex(InternalIndex index) {
377: this .index = index;
378:
379: }
380:
381: protected Object getCacheValue(Object key) {
382: return get(key);
383: }
384:
385: /* (non-Javadoc)
386: * @see com.jofti.cache.CacheLocking#getIndex()
387: */
388: public InternalIndex getIndex() {
389: return index;
390: }
391:
392: public IndexQuery addQuery(String name, IndexQuery query)
393: throws JoftiException {
394:
395: return index.getParserManager().addQuery(name, query);
396: }
397:
398: /* (non-Javadoc)
399: * @see com.jofti.api.Index#getQuery(java.lang.String)
400: */
401: public IndexQuery getQuery(String name) {
402:
403: return index.getParserManager().getQuery(name);
404: }
405: }
|