001: /**
002: * JDBM LICENSE v1.00
003: *
004: * Redistribution and use of this software and associated documentation
005: * ("Software"), with or without modification, are permitted provided
006: * that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain copyright
009: * statements and notices. Redistributions must also contain a
010: * copy of this document.
011: *
012: * 2. Redistributions in binary form must reproduce the
013: * above copyright notice, this list of conditions and the
014: * following disclaimer in the documentation and/or other
015: * materials provided with the distribution.
016: *
017: * 3. The name "JDBM" must not be used to endorse or promote
018: * products derived from this Software without prior written
019: * permission of Cees de Groot. For written permission,
020: * please contact cg@cdegroot.com.
021: *
022: * 4. Products derived from this Software may not be called "JDBM"
023: * nor may "JDBM" appear in their names without prior written
024: * permission of Cees de Groot.
025: *
026: * 5. Due credit should be given to the JDBM Project
027: * (http://jdbm.sourceforge.net/).
028: *
029: * THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
030: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
031: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
032: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
033: * CEES DE GROOT OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
034: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
035: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
036: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
037: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
038: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
039: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
040: * OF THE POSSIBILITY OF SUCH DAMAGE.
041: *
042: * Copyright 2000 (C) Cees de Groot. All Rights Reserved.
043: * Copyright 2000-2001 (C) Alex Boisvert. All Rights Reserved.
044: * Contributions are Copyright (C) 2000 by their associated contributors.
045: *
046: * $Id: CacheRecordManager.java,v 1.9 2005/06/25 23:12:32 doomdark Exp $
047: */package jdbm.recman;
048:
049: import jdbm.RecordManager;
050: import jdbm.helper.CacheEvictionException;
051: import jdbm.helper.CachePolicy;
052: import jdbm.helper.CachePolicyListener;
053: import jdbm.helper.DefaultSerializer;
054: import jdbm.helper.Serializer;
055: import jdbm.helper.WrappedRuntimeException;
056:
057: import java.io.IOException;
058: import java.util.Enumeration;
059:
060: /**
061: * A RecordManager wrapping and caching another RecordManager.
062: *
063: * @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
064: * @author <a href="cg@cdegroot.com">Cees de Groot</a>
065: * @version $Id: CacheRecordManager.java,v 1.9 2005/06/25 23:12:32 doomdark Exp $
066: */
067: public class CacheRecordManager implements RecordManager {
068:
069: /**
070: * Wrapped RecordManager
071: */
072: protected RecordManager _recman;
073:
074: /**
075: * Cache for underlying RecordManager
076: */
077: protected CachePolicy _cache;
078:
079: /**
080: * Construct a CacheRecordManager wrapping another RecordManager and
081: * using a given cache policy.
082: *
083: * @param recman Wrapped RecordManager
084: * @param cache Cache policy
085: */
086: public CacheRecordManager(RecordManager recman, CachePolicy cache) {
087: if (recman == null) {
088: throw new IllegalArgumentException(
089: "Argument 'recman' is null");
090: }
091: if (cache == null) {
092: throw new IllegalArgumentException(
093: "Argument 'cache' is null");
094: }
095: _recman = recman;
096: _cache = cache;
097:
098: _cache.addListener(new CacheListener());
099: }
100:
101: /**
102: * Get the underlying Record Manager.
103: *
104: * @return underlying RecordManager or null if CacheRecordManager has
105: * been closed.
106: */
107: public RecordManager getRecordManager() {
108: return _recman;
109: }
110:
111: /**
112: * Get the underlying cache policy
113: *
114: * @return underlying CachePolicy or null if CacheRecordManager has
115: * been closed.
116: */
117: public CachePolicy getCachePolicy() {
118: return _cache;
119: }
120:
121: /**
122: * Inserts a new record using a custom serializer.
123: *
124: * @param obj the object for the new record.
125: * @return the rowid for the new record.
126: * @throws IOException when one of the underlying I/O operations fails.
127: */
128: public long insert(Object obj) throws IOException {
129: return insert(obj, DefaultSerializer.INSTANCE);
130: }
131:
132: /**
133: * Inserts a new record using a custom serializer.
134: *
135: * @param obj the object for the new record.
136: * @param serializer a custom serializer
137: * @return the rowid for the new record.
138: * @throws IOException when one of the underlying I/O operations fails.
139: */
140: public synchronized long insert(Object obj, Serializer serializer)
141: throws IOException {
142: checkIfClosed();
143:
144: long recid = _recman.insert(obj, serializer);
145: try {
146: _cache.put(new Long(recid), new CacheEntry(recid, obj,
147: serializer, false));
148: } catch (CacheEvictionException except) {
149: throw new WrappedRuntimeException(except);
150: }
151: return recid;
152: }
153:
154: /**
155: * Deletes a record.
156: *
157: * @param recid the rowid for the record that should be deleted.
158: * @throws IOException when one of the underlying I/O operations fails.
159: */
160: public synchronized void delete(long recid) throws IOException {
161: checkIfClosed();
162:
163: _recman.delete(recid);
164: _cache.remove(new Long(recid));
165: }
166:
167: /**
168: * Updates a record using standard Java serialization.
169: *
170: * @param recid the recid for the record that is to be updated.
171: * @param obj the new object for the record.
172: * @throws IOException when one of the underlying I/O operations fails.
173: */
174: public void update(long recid, Object obj) throws IOException {
175: update(recid, obj, DefaultSerializer.INSTANCE);
176: }
177:
178: /**
179: * Updates a record using a custom serializer.
180: *
181: * @param recid the recid for the record that is to be updated.
182: * @param obj the new object for the record.
183: * @param serializer a custom serializer
184: * @throws IOException when one of the underlying I/O operations fails.
185: */
186: public synchronized void update(long recid, Object obj,
187: Serializer serializer) throws IOException {
188: CacheEntry entry;
189: Long id;
190:
191: checkIfClosed();
192:
193: id = new Long(recid);
194: try {
195: entry = (CacheEntry) _cache.get(id);
196: if (entry != null) {
197: // reuse existing cache entry
198: entry._obj = obj;
199: entry._serializer = serializer;
200: entry._isDirty = true;
201: } else {
202: _cache.put(id, new CacheEntry(recid, obj, serializer,
203: true));
204: }
205: } catch (CacheEvictionException except) {
206: throw new IOException(except.getMessage());
207: }
208: }
209:
210: /**
211: * Fetches a record using standard Java serialization.
212: *
213: * @param recid the recid for the record that must be fetched.
214: * @return the object contained in the record.
215: * @throws IOException when one of the underlying I/O operations fails.
216: */
217: public Object fetch(long recid) throws IOException {
218: return fetch(recid, DefaultSerializer.INSTANCE);
219: }
220:
221: /**
222: * Fetches a record using a custom serializer.
223: *
224: * @param recid the recid for the record that must be fetched.
225: * @param serializer a custom serializer
226: * @return the object contained in the record.
227: * @throws IOException when one of the underlying I/O operations fails.
228: */
229: public synchronized Object fetch(long recid, Serializer serializer)
230: throws IOException {
231: checkIfClosed();
232:
233: Long id = new Long(recid);
234: CacheEntry entry = (CacheEntry) _cache.get(id);
235: if (entry == null) {
236: entry = new CacheEntry(recid, null, serializer, false);
237: entry._obj = _recman.fetch(recid, serializer);
238: try {
239: _cache.put(id, entry);
240: } catch (CacheEvictionException except) {
241: throw new WrappedRuntimeException(except);
242: }
243: }
244: return entry._obj;
245: }
246:
247: /**
248: * Closes the record manager.
249: *
250: * @throws IOException when one of the underlying I/O operations fails.
251: */
252: public synchronized void close() throws IOException {
253: checkIfClosed();
254:
255: updateCacheEntries();
256: _recman.close();
257: _recman = null;
258: _cache = null;
259: }
260:
261: /**
262: * Returns the number of slots available for "root" rowids. These slots
263: * can be used to store special rowids, like rowids that point to
264: * other rowids. Root rowids are useful for bootstrapping access to
265: * a set of data.
266: */
267: public synchronized int getRootCount() {
268: checkIfClosed();
269:
270: return _recman.getRootCount();
271: }
272:
273: /**
274: * Returns the indicated root rowid.
275: *
276: * @see #getRootCount
277: */
278: public synchronized long getRoot(int id) throws IOException {
279: checkIfClosed();
280:
281: return _recman.getRoot(id);
282: }
283:
284: /**
285: * Sets the indicated root rowid.
286: *
287: * @see #getRootCount
288: */
289: public synchronized void setRoot(int id, long rowid)
290: throws IOException {
291: checkIfClosed();
292:
293: _recman.setRoot(id, rowid);
294: }
295:
296: /**
297: * Commit (make persistent) all changes since beginning of transaction.
298: */
299: public synchronized void commit() throws IOException {
300: checkIfClosed();
301: updateCacheEntries();
302: _recman.commit();
303: }
304:
305: /**
306: * Rollback (cancel) all changes since beginning of transaction.
307: */
308: public synchronized void rollback() throws IOException {
309: checkIfClosed();
310:
311: _recman.rollback();
312:
313: // discard all cache entries since we don't know which entries
314: // where part of the transaction
315: _cache.removeAll();
316: }
317:
318: /**
319: * Obtain the record id of a named object. Returns 0 if named object
320: * doesn't exist.
321: */
322: public synchronized long getNamedObject(String name)
323: throws IOException {
324: checkIfClosed();
325:
326: return _recman.getNamedObject(name);
327: }
328:
329: /**
330: * Set the record id of a named object.
331: */
332: public synchronized void setNamedObject(String name, long recid)
333: throws IOException {
334: checkIfClosed();
335:
336: _recman.setNamedObject(name, recid);
337: }
338:
339: /**
340: * Check if RecordManager has been closed. If so, throw an
341: * IllegalStateException
342: */
343: private void checkIfClosed() throws IllegalStateException {
344: if (_recman == null) {
345: throw new IllegalStateException(
346: "RecordManager has been closed");
347: }
348: }
349:
350: /**
351: * Update all dirty cache objects to the underlying RecordManager.
352: */
353: protected void updateCacheEntries() throws IOException {
354: Enumeration enume = _cache.elements();
355: while (enume.hasMoreElements()) {
356: CacheEntry entry = (CacheEntry) enume.nextElement();
357: if (entry._isDirty) {
358: _recman.update(entry._recid, entry._obj,
359: entry._serializer);
360: entry._isDirty = false;
361: }
362: }
363: }
364:
365: private class CacheEntry {
366:
367: long _recid;
368: Object _obj;
369: Serializer _serializer;
370: boolean _isDirty;
371:
372: CacheEntry(long recid, Object obj, Serializer serializer,
373: boolean isDirty) {
374: _recid = recid;
375: _obj = obj;
376: _serializer = serializer;
377: _isDirty = isDirty;
378: }
379:
380: } // class CacheEntry
381:
382: private class CacheListener implements CachePolicyListener {
383:
384: /** Notification that cache is evicting an object
385: *
386: * @arg obj object evited from cache
387: *
388: */
389: public void cacheObjectEvicted(Object obj)
390: throws CacheEvictionException {
391: CacheEntry entry = (CacheEntry) obj;
392: if (entry._isDirty) {
393: try {
394: _recman.update(entry._recid, entry._obj,
395: entry._serializer);
396: } catch (IOException except) {
397: throw new CacheEvictionException(except);
398: }
399: }
400: }
401:
402: }
403: }
|