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