163 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package org.apache.jdbm;
 | |
| 
 | |
| import javax.crypto.Cipher;
 | |
| import java.io.IOError;
 | |
| import java.io.IOException;
 | |
| import java.util.Comparator;
 | |
| import java.util.Iterator;
 | |
| 
 | |
| /**
 | |
|  * Abstract class with common cache functionality
 | |
|  */
 | |
| abstract class DBCache extends DBStore{
 | |
| 
 | |
|     static final int NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT = 1024;
 | |
| 
 | |
|     static final byte NONE = 1;
 | |
|     static final byte MRU = 2;
 | |
|     static final byte WEAK = 3;
 | |
|     static final byte SOFT = 4;
 | |
|     static final byte HARD = 5;
 | |
| 
 | |
|     static final class DirtyCacheEntry {
 | |
|         long _recid; //TODO recid is already part of _hashDirties, so this field could be removed to save memory
 | |
|         Object _obj;
 | |
|         Serializer _serializer;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Dirty status of _hash CacheEntry Values
 | |
|      */
 | |
|     final protected LongHashMap<DirtyCacheEntry> _hashDirties = new LongHashMap<DirtyCacheEntry>();
 | |
| 
 | |
|     private Serializer cachedDefaultSerializer = null;
 | |
| 
 | |
| 
 | |
|     /**
 | |
|      * Construct a CacheRecordManager wrapping another DB and
 | |
|      * using a given cache policy.
 | |
|      */
 | |
|     public DBCache(String filename, boolean readonly, boolean transactionDisabled,
 | |
|                       Cipher cipherIn, Cipher cipherOut, boolean useRandomAccessFile,
 | |
|                      boolean deleteFilesAfterClose,boolean lockingDisabled){
 | |
| 
 | |
|         super(filename, readonly, transactionDisabled,
 | |
|                 cipherIn, cipherOut, useRandomAccessFile,
 | |
|                 deleteFilesAfterClose,lockingDisabled);
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
|     @Override
 | |
|     public synchronized  Serializer defaultSerializer(){
 | |
|         if(cachedDefaultSerializer==null)
 | |
|             cachedDefaultSerializer = super.defaultSerializer();
 | |
|         return cachedDefaultSerializer;
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     boolean needsAutoCommit() {
 | |
|         return super.needsAutoCommit()||
 | |
|                 (transactionsDisabled &&  !commitInProgress && _hashDirties.size() > NUM_OF_DIRTY_RECORDS_BEFORE_AUTOCOMIT);
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     public synchronized <A> long insert(final A obj, final Serializer<A> serializer, final boolean disableCache)
 | |
|             throws IOException {
 | |
|         checkNotClosed();
 | |
| 
 | |
|         if(super.needsAutoCommit())
 | |
|             commit();
 | |
| 
 | |
|         if(disableCache)
 | |
|             return super.insert(obj, serializer, disableCache);
 | |
| 
 | |
| 
 | |
|         //prealocate recid so we have something to return
 | |
|         final long recid = super.insert(PREALOCATE_OBJ, null, disableCache);
 | |
| 
 | |
| //        super.update(recid, obj,serializer);
 | |
| 
 | |
| //        return super.insert(obj,serializer,disableCache);
 | |
| 
 | |
|         //and create new dirty record for future update
 | |
|         final DirtyCacheEntry e = new DirtyCacheEntry();
 | |
|         e._recid = recid;
 | |
|         e._obj = obj;
 | |
|         e._serializer = serializer;
 | |
|         _hashDirties.put(recid,e);
 | |
| 
 | |
|         return recid;
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
|     public synchronized void commit() {
 | |
|         try{
 | |
|             commitInProgress = true;
 | |
|             updateCacheEntries();
 | |
|             super.commit();
 | |
|         }finally {
 | |
|             commitInProgress = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public synchronized  void rollback(){
 | |
|         cachedDefaultSerializer = null;
 | |
|         _hashDirties.clear();
 | |
|         super.rollback();
 | |
|     }
 | |
| 
 | |
|     
 | |
|     private static final Comparator<DirtyCacheEntry> DIRTY_COMPARATOR = new Comparator<DirtyCacheEntry>() {
 | |
|         final public int compare(DirtyCacheEntry o1, DirtyCacheEntry o2) {
 | |
|             return (int) (o1._recid - o2._recid);
 | |
| 
 | |
|         }
 | |
|     };
 | |
|     
 | |
| 
 | |
|     /**
 | |
|      * Update all dirty cache objects to the underlying DB.
 | |
|      */
 | |
|     protected void updateCacheEntries() {
 | |
|         try {
 | |
|             synchronized(_hashDirties){
 | |
| 
 | |
|                 while(!_hashDirties.isEmpty()){
 | |
|                     //make defensive copy of values as _db.update() may trigger changes in db
 | |
|                     //and this would modify dirties again
 | |
|                     DirtyCacheEntry[] vals = new DirtyCacheEntry[_hashDirties.size()];
 | |
|                     Iterator<DirtyCacheEntry> iter = _hashDirties.valuesIterator();
 | |
| 
 | |
|                     for(int i = 0;i<vals.length;i++){
 | |
|                         vals[i] = iter.next();
 | |
|                     }
 | |
|                     iter = null;
 | |
| 
 | |
|                     java.util.Arrays.sort(vals,DIRTY_COMPARATOR);
 | |
| 
 | |
|                     
 | |
|                     for(int i = 0;i<vals.length;i++){
 | |
|                         final DirtyCacheEntry entry = vals[i];
 | |
|                         vals[i] = null;
 | |
|                         super.update(entry._recid, entry._obj, entry._serializer);
 | |
|                         _hashDirties.remove(entry._recid);
 | |
| 
 | |
|                     }
 | |
| 
 | |
|                     //update may have triggered more records to be added into dirties, so repeat until all records are written.
 | |
|                 }
 | |
|             }
 | |
|         } catch (IOException e) {
 | |
|             throw new IOError(e);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
| 
 | |
| 
 | |
| }
 |