001: package com.jofti.cache.adapter;
002:
003: import java.util.Iterator;
004: import java.util.Properties;
005:
006: import net.sf.ehcache.CacheException;
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.BaseAdaptor;
013: import com.jofti.cache.CacheAdapter;
014: import com.jofti.cache.adapter.listener.OSEventListener;
015: import com.jofti.core.InternalIndex;
016: import com.jofti.exception.JoftiException;
017: import com.jofti.util.PropertyLoader;
018: import com.opensymphony.oscache.base.NeedsRefreshException;
019: import com.opensymphony.oscache.base.events.CacheEntryEventListener;
020: import com.opensymphony.oscache.general.GeneralCacheAdministrator;
021:
022: /**
023:
024: *
025: * The adapter implementation specific to OSCache.
026: *
027: * The adapter takes care of the implementation specific details for converting
028: * between the process the index expects and the behaviour of OSCache.
029: *
030: * For OSCache this means that the behaviour where a NeedsRefreshExeption is
031: * generated on a get is no longer visible to the caller. Instead, the entry is flushed
032: * as it has expired. <p>
033: *
034: * OSCache has a full set of callbacks for the object in its cache so updates to the
035: * index are driven by these callbacks.<p>
036: *
037: * On start up this adpater will <b>not</b> try and index existing elements in the cache as OSCache provides no way to obtain a set of exsting
038: * keys.<p>
039: *
040: * @author Steve Woodcock
041: * <p>
042: * @version 1.0
043: * <p>
044: */
045:
046: public class OSCacheAdapter extends BaseAdaptor implements CacheAdapter {
047:
048: private GeneralCacheAdministrator admin = null;
049:
050: private static Log log = LogFactory.getLog(OSCacheAdapter.class);
051:
052: private String name;
053:
054: private int expiryTime = 3600; //seconds
055:
056: private OSEventListener eventListener = null;
057:
058: /**
059: * @return Returns the expiryTime.
060: */
061: public int getExpiryTime() {
062: return expiryTime;
063: }
064:
065: /**
066: * @param expiryTime The expiryTime to set.
067: */
068: public void setExpiryTime(int expiryTime) {
069: this .expiryTime = expiryTime;
070: }
071:
072: public OSCacheAdapter() {
073: }
074:
075: public OSCacheAdapter(Object cache) {
076: if (!(cache instanceof GeneralCacheAdministrator)) {
077: throw new RuntimeException(
078: "OSCacheAdapter requires GeneralCacheAdministrator ");
079: }
080: this .admin = (GeneralCacheAdministrator) cache;
081: }
082:
083: public void setCacheImpl(Object cache) {
084: this .admin = (GeneralCacheAdministrator) cache;
085:
086: }
087:
088: /* (non-Javadoc)
089: * @see com.jofti.api.IndexCache#get(java.lang.Object)
090: */
091: public Object get(Object key) {
092: if (key != null && (!(key instanceof String))) {
093: log.warn("key value for OSCache must by of type String "
094: + key);
095: return null;
096: }
097: Object obj = null;
098: synchronized (getCacheLock(key)) {
099: try {
100: obj = admin.getFromCache((String) key, expiryTime);
101: } catch (NeedsRefreshException nre) {
102: // we do this because of the bizarre way OSCache throws
103: // exceptions for gets
104: admin.cancelUpdate((String) key);
105: // it has expired so lets flush the entry
106: admin.flushEntry((String) key);
107:
108: }
109: }
110: // we return null here if we got an exception because it is consistent
111: // with most other cache implementations
112: return obj;
113: }
114:
115: /* (non-Javadoc)
116: * @see com.jofti.api.IndexCache#put(java.lang.Object, java.lang.Object)
117: */
118: public void put(Object key, Object value) throws JoftiException {
119:
120: if (key != null && (!(key instanceof String))) {
121: throw new JoftiException(
122: "key value for OSCache must by of type String "
123: + key);
124: }
125:
126: // get a lock for the key that we are trying to add
127: // we do not care about other parts of the cache or the index
128: synchronized (getCacheLock(key)) {
129:
130: // rely on call back listener to populate index entry
131: admin.putInCache((String) key, value);
132:
133: }
134:
135: }
136:
137: /**
138: * Removes the element which matches the key.
139: * <p>
140: * If no element matches, nothing is removed and no Exception is thrown.
141: *
142: * @param key
143: * the key of the element to remove
144: * @throws CacheException
145: */
146: public void remove(Object key) throws JoftiException {
147:
148: // remove this entry and rely on call back listener to remove index entry
149: if (key != null && (!(key instanceof String))) {
150: throw new JoftiException(
151: "key value for OSCache must by of type String "
152: + key);
153: }
154: admin.flushEntry((String) key);
155:
156: }
157:
158: /**
159: * Remove all elements in the cache, but leave the cache
160: * in a useable state.
161: * @throws CacheException
162: */
163: public void removeAll() throws JoftiException {
164: //rely on call back listener to remove index entry
165: admin.flushAll();
166: }
167:
168: /* (non-Javadoc)
169: * @see com.jofti.cache.LifeCycleAdapter#init(java.util.Properties)
170: */
171: public synchronized void init(Properties properties)
172: throws JoftiException {
173: try {
174:
175: String cacheConfigFile = null;
176: if (properties != null) {
177: String key = null;
178: for (Iterator it = properties.keySet().iterator(); it
179: .hasNext();) {
180: key = (String) it.next();
181: if (MUTABLE_VALUES.equalsIgnoreCase(key)) {
182: checkMutable = Boolean.valueOf(
183: properties.getProperty(key))
184: .booleanValue();
185: if (log.isInfoEnabled()) {
186: log.info("Mutability checking is set to "
187: + checkMutable);
188: }
189: }
190: if ("file".equalsIgnoreCase(key)) {
191: cacheConfigFile = properties.getProperty(key);
192: }
193: }
194: }
195: log.debug("looking up GeneralCacheAdministrator");
196: if (admin == null) {
197:
198: if (cacheConfigFile == null) {
199: log
200: .debug("not found - creating default GeneralCacheAdministrator");
201: admin = new GeneralCacheAdministrator();
202: } else {
203: // we need to construct a properties object
204: log
205: .debug("not found - creating default GeneralCacheAdministrator");
206: Properties props = null;
207: try {
208: props = PropertyLoader
209: .loadProperties(cacheConfigFile);
210: } catch (Throwable t) {
211: log.warn("Unable to load config file "
212: + cacheConfigFile + " from classpath");
213: }
214: if (props == null) {
215: throw new JoftiException(
216: "Unable to load config file "
217: + cacheConfigFile
218: + " from classpath");
219: }
220: admin = new GeneralCacheAdministrator(props);
221: }
222: } else {
223: log.debug("using supplied GeneralCacheAdministrator");
224: }
225: if (properties != null) {
226: String expiryParam = properties
227: .getProperty("expiryTime");
228: if (expiryParam != null) {
229: try {
230: expiryTime = Integer.parseInt(expiryParam);
231: } catch (NumberFormatException nfe) {
232: log.error(
233: "Unable to set expiry time - using default of "
234: + expiryTime, nfe);
235: }
236: }
237: }
238:
239: eventListener = new OSEventListener(this );
240:
241: admin.getCache().addCacheEventListener(eventListener,
242: CacheEntryEventListener.class);
243:
244: } catch (Exception e) {
245: throw new JoftiException(e);
246: }
247:
248: }
249:
250: /* (non-Javadoc)
251: * @see com.jofti.cache.LifeCycleAdapter#destroy()
252: */
253: public void destroy() throws JoftiException {
254: if (admin != null) {
255: admin.destroy();
256:
257: }
258: if (index != null) {
259: index.removeAll();
260: }
261:
262: }
263:
264: public String getName() {
265: return name;
266: }
267:
268: public void setName(String name) {
269: this .name = name;
270: }
271:
272: public String toString() {
273: return "OSCache(" + getName() + ')';
274: }
275:
276: /* (non-Javadoc)
277: * @see com.jofti.api.IndexCache#getCacheImpl()
278: */
279: public Object getCacheImpl() {
280: return admin;
281: }
282:
283: /* (non-Javadoc)
284: * @see com.jofti.cache.CacheAdapter#setInternalIndex(com.jofti.core.InternalIndex)
285: */
286: public void setInternalIndex(InternalIndex index) {
287: this .index = index;
288:
289: }
290:
291: /* (non-Javadoc)
292: * @see com.jofti.cache.CacheAdapter#start()
293: */
294: public void start() throws JoftiException {
295:
296: // do nothing here
297:
298: }
299:
300: protected Object getCacheValue(Object key) {
301: return get(key);
302: }
303:
304: /* (non-Javadoc)
305: * @see com.jofti.cache.CacheLocking#getIndex()
306: */
307: public InternalIndex getIndex() {
308: return index;
309: }
310:
311: public IndexQuery addQuery(String name, IndexQuery query)
312: throws JoftiException {
313:
314: return index.getParserManager().addQuery(name, query);
315: }
316:
317: /* (non-Javadoc)
318: * @see com.jofti.api.Index#getQuery(java.lang.String)
319: */
320: public IndexQuery getQuery(String name) {
321:
322: return index.getParserManager().getQuery(name);
323: }
324: }
|