alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.mdr.persistence.btreeimpl.btreestorage;

import java.io.*;
import java.text.*;
import java.util.*;

import org.netbeans.mdr.persistence.btreeimpl.btreeindex.*;
import org.netbeans.mdr.persistence.*;
import org.netbeans.mdr.util.Logger;

/**
* This is the primary index for the btree implementation of Storage.
* It consists of three files:
* 

* The data file, with an extension of ".btd", which contains the * serialized versions of the Streamable objects. This is accessed via * the BtreeDatafile class. *

* The index file, with an extension of ".btx", which translates MOFIDs to the * corresponding offset in the data file. This is accessed via the * SinglevaluedBtree class. *

* The transactional log file has the extnsion ".btb". It exists only * between the time a trasaction starts to be comitted and the time it * is fully comitted or rolled back. *

* The data file contains enough information to regenerate the index file if the * index file is lost or damaged. If the index file is missing when the btree * database is opened, it is silently regenerated. *

* A Btree storage is transactional. This is implemented using the logging * feature of the FileCache. Objects stored in a Btree storage have identity * in memory. That is, fetching the same object multiple times will result in * multiple references to the same object, not mutilple copies. This is * implemented by MDRCache. *

* Performance of the Btree can be tuned by adjusting these parameters: *

* PAGE_SIZE is the size of the pages in the file cache. *

* FILE_CACHE_SIZE in the number of pages cached in memory. Increasing this * will minimize the amount of reading done from the btree files, though at * the cost of decreased memory for other purposes. *

* MDR_CACHE_SIZE is the number of currently unreferenced persistent objects * kept in memory. Increasing this will reduce the number of reads done from * the btree storage, though again at the cost of decreased memory for other * purposes. *

* MDR_CACHE_THRESHHOLD is the number of changed objects to keep in memory * before saving them to disk. Increasing this will reduce the amount of disk * I/O done, though yet again at the cost of decreased memory for other purposes. */ public class BtreeDatabase implements SinglevaluedIndex, MDRCache.OverflowHandler { /* tuning parameters explained above */ static final int PAGE_SIZE = 2048; static final int FILE_CACHE_SIZE = 128; static final int MDR_CACHE_SIZE = 2048; static final int MDR_CACHE_THRESHHOLD = 1000; /* number of times database has been modified */ private int modificationLevel; /* Name of repository */ private String repositoryName; /* Data file */ private BtreeDataFile dataFile; /* Index file */ private SinglevaluedIndex indexFile; /* File cache containing these two files */ private FileCache fileCache; /* Cache of repository objects */ private MDRCache cache; /* index of classes */ private CounterIndex classIndex; /* whether the class index is dirty */ private boolean classIndexChanged; /* class index type */ private static String CLASS_INDEX_TYPE = "org.netbeans.mdr.persistence.btreeimpl.btreestorage.CounterIndex"; /* table of class names */ private ArrayList classes; /* index of indexes */ private MofidIndex indexIndex; /* the storage which created us */ private BtreeStorage myStorage; /* true if a save error has occured */ private boolean saveFailed = false; /* stores data serialized by the last @link #writeStreamable */ private byte [] baoStrmToBytes; /* Streams used to write data */ private ByteArrayOutputStream baoStrm = new ByteArrayOutputStream(); private DataOutputStream daoStrm = new DataOutputStream(baoStrm); private TransactionCache transactionCache = null; /* Logging stream. Note -- this should be replaced by the standard ** logger */ private PrintStream loggingStream; /** create a BtreeDatabase and create/open the files * @param name name of database, which is used to name the files * @param parent the Storage for which this object is the primary index * @param isNew true if the database is being created */ BtreeDatabase(String name, BtreeStorage parent, boolean isNew) throws StorageException { repositoryName = name; myStorage = parent; classes = new ArrayList(); try { classes.add(Class.forName(CLASS_INDEX_TYPE)); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex.getMessage()); } open(isNew); } private static final int DFL = 0; private static final int IFL = 1; private static final int LFL = 2; /* construct names of repository files */ private static String[] getFileNames(String base) { String names[] = new String[3]; names[DFL] = base + ".btd"; names[IFL] = base + ".btx"; names[LFL] = base + ".btb"; return names; } /** See if repository currenly exists */ static boolean exists(String base) { String names[] = getFileNames(base); return (new File(names[DFL])).exists() || (new File(names[IFL])).exists() || (new File(names[LFL])).exists(); } /** delete the btree repository. * @return true if, at method end, there is no repository. false if * the repository exists but could not be deleted for any reason */ static boolean delete(String base) { String names[] = getFileNames(base); return deleteFile(names[DFL]) & deleteFile(names[IFL]) & deleteFile(names[LFL]); } /* Delete a file */ private static boolean deleteFile(String name) { File file = new File(name); if (!file.exists()) return true; else return file.delete(); } /* Rename database */ static void rename(String from, String to) throws StorageException { if (exists(to)) { throw new StorageBadRequestException( MessageFormat.format("Btree repository {0} already exists", new Object[] {to} )); } String fromNames[] = getFileNames(from); String toNames[] = getFileNames(to); boolean success = (new File(fromNames[DFL])).renameTo(new File(toNames[DFL])); if (success) { success = (new File(fromNames[IFL])).renameTo(new File(toNames[IFL])); } if (success) { File fromLog = new File(fromNames[LFL]); if (fromLog.exists()) success = fromLog.renameTo(new File(toNames[DFL])); } if (!success) { throw new StorageBadRequestException( MessageFormat.format( "Unable to rename btree repository {0} to {1}", new Object[] {from, to} ) ); } } private int getIntProperty(String propertyName, int defaultValue) { String value = (String) myStorage.getProperty(propertyName); int result = defaultValue; if (value != null) { try { result = Integer.parseInt(value); } catch (NumberFormatException e) { Logger.getDefault().log("Error getting value of " + propertyName + " storage property: " + e.getMessage()); } } return result; } // open or create the database, depending on the value of isNew private void open(boolean isNew) throws StorageException { boolean rebuildIndex = false; cache = new MDRCache(getIntProperty(BtreeFactory.CACHE_SIZE, MDR_CACHE_SIZE), this, getIntProperty(BtreeFactory.CACHE_THRESHHOLD, MDR_CACHE_THRESHHOLD), (Map) myStorage.getProperty(BtreeFactory.CACHE_INSTANCE)); transactionCache = new TransactionCache (); String names[] = getFileNames(repositoryName); String fileNames[] = {names[DFL], names[IFL]}; if (isNew) { FileHeader hdr = FileHeader.createFiles(new String[] {names[IFL]}, PAGE_SIZE, false); BtreeDataFile.create(this.myStorage, names[DFL], hdr, PAGE_SIZE, false); } else { File dfl = new File(names[DFL]); File ifl = new File(names[IFL]); if (dfl.exists() && ! ifl.exists()) { /* Create empty index file */ FileHeader hdr = null; try { RandomAccessFile draf = new RandomAccessFile(dfl, "r"); hdr = new FileHeader(draf); draf.close(); } catch (IOException ex) { throw new StorageIOException(ex); } hdr.addFiles(new String[] {names[IFL]}, PAGE_SIZE, false); rebuildIndex = true; } } fileCache = new FileCache(fileNames, names[LFL]); boolean failure = true; try { dataFile = new BtreeDataFile(this.myStorage, fileCache, 0); myStorage.gen = dataFile; BtreeFileSource source = new BtreeFileSource(1, fileCache, PAGE_SIZE, isNew || rebuildIndex, dataFile, myStorage); indexFile = new SinglevaluedBtree(repositoryName, Storage.EntryType.MOFID, Storage.EntryType.INT, source); if (rebuildIndex) { rebuildIndexFile(); save (true); } fetchClassIndex(); if (isNew) { save (true); } failure = false; } finally { if (failure) fileCache.abort(); } } /** Make a copy of the database. Note that since the copy is done * record by record rather than byte by byte, the data file will * be compressed. * @param target name for copied database */ void copy(String target) throws StorageException { FileCache copyCache = null; BtreeDataFile copyFile; if (cache.hasChanges()) { throw new StorageBadRequestException( MessageFormat.format( "There are changes to repository {0} that have not been committed", new Object[] {target} )); } if (exists(target)) { throw new StorageBadRequestException( MessageFormat.format("Btree repository {0} already exists", new Object[] {target} )); } String copyNames[] = getFileNames(target); String fileNames[] = {copyNames[DFL]}; BtreeDataFile.create( this.myStorage, copyNames[DFL], new FileHeader(), PAGE_SIZE, false); try { copyCache = new FileCache(fileNames, copyNames[LFL]); copyFile = new BtreeDataFile(this.myStorage, copyCache, 0); dataFile.copy(copyFile); copyCache.commit(); copyCache.close(); // Open btree database to create index file BtreeDatabase db = new BtreeDatabase(target, myStorage, false); db.close(); } catch (StorageException ex) { if (copyCache != null) copyCache.close(); delete(target); throw ex; } } /** Compress repository. We do this by copying to a temporary repository, * deleting the current repository, and renaming the temporary repository * to our name. */ public synchronized void compress() throws StorageException { if (cache.hasChanges()) { throw new StorageBadRequestException( MessageFormat.format( "There are changes to repository {0} that have not been committed", new Object[] {repositoryName} )); } String tempName = "tmp" + (new Random().nextInt()); copy(tempName); closeFiles(); delete(repositoryName); rename(tempName, repositoryName); open(false); } /** Rebuild the index file. Since we iterate through the data file * to find all of its records, all changes to the data file must * already have been committed. The index file is assumed to be * empty at this point. */ private void rebuildIndexFile() throws StorageException { Iterator iter = dataFile.iterator( BtreeDataFile.ITERATE_NORMAL_EXTENTS); while (iter.hasNext()) { NormalBtreeExtent ext = (NormalBtreeExtent)iter.next(); MOFID k = this.myStorage.readMOFIDData (new ByteArrayInputStream (ext.key)); indexFile.add(k, new Integer(ext.myChunkNum)); } } /** returns the number of objects in the repository * */ public synchronized int size() { return dataFile.size() + cache.numberNew() - cache.numberDeleted(); } /** Returns the unique name of the index in the Storage. * @return base file name for repository */ public String getName() { return repositoryName; }; /** Returns the type of values indexed by this index. * @return EntryType.STREAMABLE */ public Storage.EntryType getValueType() { return Storage.EntryType.STREAMABLE; } /** Returns the type of keys in index. * @return EntryType.MOFID */ public Storage.EntryType getKeyType() { return Storage.EntryType.MOFID; } /** Close repository */ synchronized void close() throws StorageException { closeFiles(); cache = null; transactionCache = null; } /** Close all files */ synchronized void closeFiles() throws StorageException { modificationLevel++; fileCache.abort(); fileCache = null; dataFile = null; indexFile = null; } /** cache has reached threshhold */ public void cacheThreshholdReached(MDRCache cach, int size) throws StorageException { saveChanges(); } /** Commits changes to transaction cache, * if cache treshold is reached, flushes cache to disk. */ public synchronized void commitChanges() throws StorageException { save(false); if (transactionCache.tresholdReached ()) { try { //System.err.println("Threshhold reached!"); fileCache.commit(); transactionCache.clear (); } catch (StorageException ex) { saveFailed = true; throw(ex); } // catch } else { transactionCache.commit (); } } /** * Called on exit, commited data cached in transaction cache need to be written to disk. */ public synchronized void shutDown() throws StorageException { try { fileCache.commit(); } catch (StorageException ex) { saveFailed = true; throw(ex); } // catch } /** save all changes to disk without comitting */ public synchronized void saveChanges() throws StorageException { save(false); } /* save all changes to disk, optionally comitting */ private void save(boolean commit) throws StorageException { MOFID id; Object value; if (saveFailed) { // If commit has failed previously, allowing this commit would // save invalid data throw new StorageBadRequestException( "A save of this repository has failed previously. Allowing this commit to proceed would potentially corrupt persistent data."); } try { modificationLevel++; StorageException writeError; classIndexChanged = false; // First, persistently delete everything that needs it Iterator delIter = cache.getDeleted().iterator(); while (delIter.hasNext()) { id = (MOFID)delIter.next(); removeRecord(id); delIter.remove(); transactionCache.addDeleted(id); } // Next, modify what needs modification Iterator dirtyIter = cache.getDirty().iterator(); while (dirtyIter.hasNext()) { id = (MOFID)dirtyIter.next(); value = cache.get(id); writeError = replaceRecord(id, value); if (writeError == null) { dirtyIter.remove(); transactionCache.addReplaced(id, baoStrmToBytes); } else /*if (commit)*/ { throw writeError; } // else if (loggingStream != null) { // loggingStream.println("Error writing streamable: " + // writeError.toString()); // } } // Last, add what needs adding Iterator newIter = cache.getNew().iterator(); while (newIter.hasNext()) { id = (MOFID)newIter.next(); value = cache.get(id); writeError = addRecord(id, value); if (writeError == null) { newIter.remove(); transactionCache.addInserted(id, baoStrmToBytes); } else /*if (commit)*/ { throw writeError; } // else if (loggingStream != null) { // loggingStream.println("Error writing streamable: " + // writeError.toString()); // } } if (classIndexChanged) { writeError = replaceRecord(BtreeFactory.classIndexId, classIndex); transactionCache.addReplaced(BtreeFactory.classIndexId, baoStrmToBytes); if (writeError != null) throw writeError; classIndexChanged = false; } // commit to disk if (commit) { fileCache.commit(); transactionCache.clear (); } } catch (StorageException ex) { // Record that commit has failed. saveFailed = true; throw(ex); } finally { cache.updateSize(); } } /** roll back all changes */ public synchronized void rollbackChanges() throws StorageException { modificationLevel++; Integer offset; int dataOffset; StorageException writeError; // iterator have to be obtained before closeFiles is called (it drops transitionCache) TransactionCache.CacheIterator iter = transactionCache.iterator (); long counter = dataFile.getMofIdCounter (); boolean dataCommited = transactionCache.containsCommitedData (); // throw away data in file cache close(); open(false); // redo operations commited to transaction cache try { while (iter.hasNext ()) { TransactionCache.Record rec = iter.next (); switch (rec.op) { case TransactionCache.OP_DELETE: removeRecord(rec.id); break; case TransactionCache.OP_REPLACE: offset = (Integer)indexFile.get(rec.id); dataOffset = dataFile.replace( offset.intValue(), rec.id.getSerialNumber(), rec.value); indexFile.replace(rec.id, new Integer(dataOffset)); break; case TransactionCache.OP_INSERT: dataOffset = dataFile.put(rec.id.getSerialNumber(), rec.value); indexFile.add(rec.id, new Integer(dataOffset)); break; } // switch } // while // reset mof id generator to the value before rollback dataFile.setMofIdCounter (counter); fileCache.commit(); } catch (StorageException ex) { // Record that commit has failed. saveFailed = true; throw(ex); } } /** Deletes a repository record * @param key -- a String * @return true if record exists */ public synchronized boolean remove(Object key) throws StorageException { return remove(makeMOFID(key)); } /** Deletes a repository record based on a MOFID * @param mKey * @return true if record exists */ public synchronized boolean remove(MOFID mKey) throws StorageException { modificationLevel++; if (!exists(mKey)) { return false; } else { cache.remove(mKey); return true; } } /** Adds a repository record, throwing an exception if it already exists * @param key -- a String * @param value */ public synchronized void add(Object key, Object value) throws StorageException { add(makeMOFID(key), value); } /** Adds a repository record, throwing an exception if it already exists * @param mKey * @param value */ public synchronized void add(MOFID mKey, Object value) throws StorageException { modificationLevel++; if (exists(mKey)) { throw new StorageBadRequestException( MessageFormat.format("Record with key {0} already exists", new Object[] {mKey} ) ); } addToCache(mKey, value); } /* Add object to cache */ private void addToCache(MOFID key, Object value) throws StorageException { cache.put(key, value); cache.setNew(key); } /** Replaces the original value associated with the specified key in this * index with new value. If no value was associated with this key prior * to this call StorageBadRequestException is thrown. * @param key * @param value * @throws StorageException */ public synchronized void replace(Object key, Object value) throws StorageException { replace(makeMOFID(key), value); } /** Replaces the original value associated with the specified key in this * index with new value. If no value was associated with this key prior * to this call StorageBadRequestException is thrown. * @param mKey * @param value * @throws StorageException */ public synchronized void replace(MOFID mKey, Object value) throws StorageException { modificationLevel++; if (!exists(mKey)) { noSuchRecord(mKey); } replaceInCache(mKey, value); } /* Replace object in cache */ private void replaceInCache(MOFID key, Object value) throws StorageException { boolean isNew = cache.isNew(key); cache.replace(key, value); if (isNew) cache.setNew(key); else cache.setDirty(key); } /** Adds or replaces a repository record * @param key -- a String * @param value * @return always null */ public synchronized boolean put(Object key, Object value) throws StorageException { modificationLevel++; MOFID mKey = makeMOFID(key); if (!exists(mKey)) { addToCache(mKey, value); return false; } else { replaceInCache(mKey, value); return true; } } /** Gets a record from the repository. Returns null if none exists. * @param key -- a String * @return value associated with specified key, * or null if there was no mapping for key */ public synchronized Object getIfExists(Object key)throws StorageException { return getIfExists(makeMOFID(key)); } /** Like getIfExists, since we don't return keys */ public synchronized Object getObjectIfExists(Object key, SinglevaluedIndex dummy) throws StorageException { return getIfExists(key); } /** Gets a record from the repository. Returns null if none exists. * @param mKey * @return value associated with specified key, * or null if there was no mapping for key */ public synchronized Object getIfExists(MOFID mKey)throws StorageException { Object retval; retval = cache.get(mKey); if (retval == null) { if (cache.isDeleted(mKey)) { return null; } retval = getRecord(mKey); if (retval != null) { cache.put(mKey, retval); } } return retval; } /** Gets a record from the repository. Throws an exception if none exists. * @param key * @return value associated with specified key, * or null if there was no mapping for key */ public synchronized Object get(Object key)throws StorageException { Object retval = getIfExists(key); if (retval == null) { noSuchRecord(key); } return retval; } /** Like get, since we don't return keys */ public synchronized Object getObject(Object key, SinglevaluedIndex dummy) throws StorageException { return get(key); } /** Gets a record from the repository. Throws an exception if none exists. * @param mKey * @return value associated with specified key, * or null if there was no mapping for key */ public synchronized Object get(MOFID mKey)throws StorageException { Object retval = getIfExists(mKey); if (retval == null) { noSuchRecord(mKey); } return retval; } public synchronized Collection queryByKeyPrefix (Object prefix, SinglevaluedIndex repos) { throw new UnsupportedOperationException (); } /* Check if the record with the given key exists, either in the cache * or in the files */ private boolean exists(MOFID key) throws StorageException { if (cache.get(key) != null) { return true; } else if (cache.isDeleted(key)) { return false; } else { return indexFile.getIfExists(key) != null; } } /* Removes the record associated in the index with specified key. * or null if there was no mapping for key */ private boolean removeRecord (MOFID mKey) throws StorageException { Integer offset = (Integer)indexFile.getIfExists(mKey); if (offset == null) { noSuchRecord(mKey); } indexFile.remove(mKey); dataFile.remove(offset.intValue(), mKey.getSerialNumber()); return true; } /** Mark that the object has changed, and so needs to be saved on commit * @param key key of object whch changed (a String) */ public synchronized void objectStateChanged(Object key) throws StorageException { objectStateChanged(makeMOFID(key)); } /** Mark that the object has changed, and so needs to be saved on commit * @param mKey key of object whch changed */ public synchronized void objectStateChanged(MOFID mKey) throws StorageException { modificationLevel++; cache.setDirty(mKey); } /** Fetch an index by name * @param name name of index */ synchronized Object fetchIndex(String name) throws StorageException { fetchIndexIndex(); MOFID indexID = indexIndex.get(name); return (indexID == null) ? null : get(indexID); } /** drop an index by name * @param name name of index */ synchronized void dropIndex(String name) throws StorageException { modificationLevel++; fetchIndexIndex(); MOFID indexId = indexIndex.get(name); indexIndex.remove(name); objectStateChanged(BtreeFactory.indexIndexId); remove(indexId); } /** List all index names * @return an array of all the index names in alphabetical order */ public synchronized String [] listIndexes() throws StorageException { fetchIndexIndex(); String retval[] = indexIndex.listNames(); Arrays.sort(retval); return retval; } /** Add a new index * @param name name of index * @param index the index object * @param mID the MOFID of the index */ synchronized void addIndex(String name, Object index, MOFID mID) throws StorageException { modificationLevel++; add(mID, index); fetchIndexIndex(); indexIndex.add(name, mID); objectStateChanged(BtreeFactory.indexIndexId); } /* tell MOFIDs where they're being written */ private static MofidGenerator currentlyStreamingMofidGenerator = null; static MofidGenerator getCurrentlyStreamingMofidGenerator() { return currentlyStreamingMofidGenerator; } /* serialize an object to output stream, return any exception */ private StorageException writeStreamable(Streamable obj) { MofidGenerator previous = currentlyStreamingMofidGenerator; currentlyStreamingMofidGenerator = dataFile; try { baoStrm.reset(); daoStrm.writeInt(getClassCode(obj.getClass())); obj.write(daoStrm); baoStrmToBytes = baoStrm.toByteArray(); } catch (StorageException ex) { return ex; } catch (IOException ioException) { return (StorageException) Logger.getDefault().annotate(new StorageIOException(ioException), ioException); } catch (RuntimeException th) { return (StorageException) Logger.getDefault().annotate(new StorageTransientDataException( th.getClass().getName() + ": " + th.getMessage()), th); } finally { currentlyStreamingMofidGenerator = previous; } // it worked return null; } private Streamable readStreamable (DataInputStream stream) throws StorageException { int classCode; Streamable data; try { classCode = stream.readInt(); } catch (IOException ex) { throw new StorageIOException(ex); } try { Class cls = (Class)classes.get(classCode); data = (Streamable)cls.newInstance(); } catch (Exception ex) { throw new StoragePersistentDataException(ex.getMessage()); } if (data instanceof StorageClient) { ((StorageClient)data).setStorage(myStorage); } data.read(stream); return data; } /** write a record to the files. * @return null if no error occurs. * An exception if one occurs serializing the value * @throws StorageException if any other error occurs */ private StorageException addRecord(MOFID mKey, Object value) throws StorageException { StorageException ex = writeStreamable((Streamable)value); if (ex != null) return ex; int dataOffset = dataFile.put(mKey.getSerialNumber(), baoStrmToBytes); indexFile.add(mKey, new Integer(dataOffset)); return null; } /* get the code number for a class */ int getClassCode(Class cls) throws StorageException{ String className = cls.getName(); fetchClassIndex(); Integer i = classIndex.getIf(className); if (i != null) { return i.intValue(); } else { int code = classIndex.add(className); classes.add(code, cls); classIndexChanged = true; return code; } } /** replace a record in the files. * @return null if no error occurs. * An exception if one occurs serializing the value * @throws StorageException if any other error occurs */ private StorageException replaceRecord(MOFID mKey,Object value)throws StorageException { StorageException ex = writeStreamable((Streamable)value); if (ex != null) return ex; Integer offset = (Integer)indexFile.get(mKey); int dataOffset = dataFile.replace( offset.intValue(), mKey.getSerialNumber(), baoStrmToBytes); indexFile.replace(mKey, new Integer(dataOffset)); return null; } /* reports that no such record exists */ private void noSuchRecord(Object key) throws StorageException { throw new StorageBadRequestException( MessageFormat.format("No record exists with key {0}", new Object[] {key} ) ); } /* read a record from the files */ private Object getRecord(MOFID mKey) throws StorageException { Integer offset = (Integer)indexFile.getIfExists(mKey); if (offset == null) { return null; } Streamable data; InputStream strm = dataFile.get(offset.intValue(), mKey.getSerialNumber()); try { DataInputStream inStream = new DataInputStream(strm); data = this.readStreamable (inStream); } finally { try { strm.close(); } catch (IOException ex) { throw new StorageIOException(ex); } } return data; } /* fetch the index index */ private void fetchIndexIndex() throws StorageException { if (indexIndex == null) { indexIndex = (MofidIndex)getIfExists(BtreeFactory.indexIndexId); if (indexIndex == null) { indexIndex = new MofidIndex(this.myStorage); add(BtreeFactory.indexIndexId, indexIndex); } indexIndex.setName("Index of secondary Indexes"); } } /* fetch the class index */ private void fetchClassIndex() throws StorageException { if (classIndex == null) { classIndex = (CounterIndex)getIfExists(BtreeFactory.classIndexId); if (classIndex == null) { classIndex = new CounterIndex(); add(BtreeFactory.classIndexId, classIndex); // pre-load the class for the class index itself classIndex.add(CLASS_INDEX_TYPE); } else { Iterator itr = classIndex.iterator(); while (itr.hasNext()) { Map.Entry ent = (Map.Entry)itr.next(); try { int code = ((Integer)ent.getValue()).intValue(); while (classes.size() < code + 1) { classes.add(null); } classes.set(code, Class.forName((String)ent.getKey())); } catch (Exception ex) { throw new StoragePersistentDataException( ex.getMessage()); } } } classIndex.setName("Index of stored classes"); } } /** Returns a set view of the keys contained in this index. * Returned collection is read only and may not be modified. * @return the set of all MOFIDs which are keys for the repository * */ public java.util.Set keySet() throws StorageException { return new Keys(); } /** Returns a collection view of the values contained in the repository. * Returned collection is read only and may not be modified. * @return all objects stored in the repository */ public java.util.Collection values () throws StorageException { return new Values(); } /** Set our logging stream */ public synchronized void setLoggingStream(PrintStream strm) { loggingStream = strm; } /** Return the MOFID generator for this repository */ public synchronized MofidGenerator getMofidGenerator() { return dataFile; } private Map mofidMap = null; /** Return the map of MOFID UUIDs we know about */ public synchronized Map getMofidMap() { // When we federae repositories, we'll need to know about all // of the federates. For now, there's only us. if (mofidMap == null) { mofidMap = new HashMap(); mofidMap.put(dataFile.getMofidPrefix(), dataFile); } return mofidMap; } /** Check consistency of btree database * @param strm where to write inconsistencies * @return number of errors encountered */ public synchronized int checkConsistency(PrintWriter strm) throws StorageException { /* Check consistency of data file */ int numErrs = dataFile.dump(BtreeDataFile.DUMP_CONSISTENTCY_INFO, 0, false, strm); /* Check that each key in the data file is properly in the index file */ Iterator recordIter = dataFile.iterator( BtreeDataFile.ITERATE_NORMAL_EXTENTS); while (recordIter.hasNext()) { NormalBtreeExtent ext = (NormalBtreeExtent)recordIter.next(); MOFID mKey = this.myStorage.readMOFIDData (new ByteArrayInputStream (ext.key)); Integer offset = (Integer)indexFile.getIfExists(mKey); if (offset == null) { strm.println("ID " + mKey + " is not in the index file."); numErrs++; } else if (offset.intValue() != ext.myChunkNum) { strm.println("ID " + mKey + " has differring offsets: " + ext.myChunkNum + " and " + offset.intValue()); numErrs++; } } /* Check that cache is consistent with the files */ Iterator actIter = cache.iterateActive(); while (actIter.hasNext()) { MOFID key = (MOFID)actIter.next(); if (cache.isDeleted(key)) { if (!cache.isNew(key)) { strm.println( "ID " + key + " is deleted and active but not new"); numErrs++; } } else { if (cache.isNew(key)) { if (inIndexFile(key)) { strm.println( "ID " + key + " is new but in the index file"); numErrs++; } } else { if (!inIndexFile(key)) { strm.println( "ID " + key + " exists but is not in the index file"); numErrs++; } } } } Iterator delIter = cache.iterateDeleted(); while (delIter.hasNext()) { MOFID key = (MOFID)delIter.next(); if (!inIndexFile(key)) { strm.println( "ID " + key + " is deleted but not in the index file"); numErrs++; } } numErrs += ((Btree)indexFile).consistencyCheck(strm); strm.println("" + numErrs + " error(s) detected."); strm.println(); cache.showStats(strm); strm.println(); fileCache.showStats(strm); strm.println(); strm.flush(); return numErrs; } /* see if MOFID is in index file */ private boolean inIndexFile(MOFID key) throws StorageException { return indexFile.getIfExists(key) != null; } /* Make a MOFID from a String key */ private MOFID makeMOFID(Object key) { if (key instanceof MOFID) return (MOFID)key; else throw new IllegalArgumentException ("Argument must be of org.netbeans.mdr.persistence.MOFID type"); } /** This will iterate over all keys in the repository */ class KeyIterator implements Iterator { /* the level of the database when we were created */ private int iterModLevel; /* iterate records in data file */ private Iterator fileIter; /* Iterate new objects in cache */ private Iterator newIter; /* next key to return */ private MOFID nextKey; /** Create the iterator * @param internal if true, return MOFIDs instead of strings */ KeyIterator(boolean internal) { fileIter = dataFile.iterator(BtreeDataFile.ITERATE_KEYS); newIter = null; iterModLevel = modificationLevel; getNextKey(); } /* Get the next key, first from the file, then from new objects in the cache */ private void getNextKey() { nextKey = null; synchronized(BtreeDatabase.this) { checkModLevel(); while (fileIter.hasNext()) { MOFID fileKey = (MOFID)fileIter.next(); if (!cache.isDeleted(fileKey)) { nextKey = fileKey; return; } } if (newIter == null) { newIter = cache.iterateNew(); } if (newIter.hasNext()) { nextKey = (MOFID)newIter.next(); } } } /** Is there another key? */ public synchronized boolean hasNext() { return nextKey != null; } /** Get the next key */ public synchronized Object next() { Object current = nextKey; getNextKey(); return current; } /** Remove is unsupported */ public void remove() { /* an optional operation which we do not support */ throw new UnsupportedOperationException( "Remove is not supported"); } private void checkModLevel() { if (iterModLevel != modificationLevel) { throw new ConcurrentModificationException( "Database had been modified"); } } } /** This will iterate over all objects in the repository */ class ValueIterator implements Iterator { private Iterator keyIter; ValueIterator() { synchronized (BtreeDatabase.this) { keyIter = new KeyIterator(true); } } /** returns true if there is another value to iterate over * @return true if there is another value to iterate over */ public boolean hasNext() { return keyIter.hasNext(); } /** Returns the next value * @return the next value */ public Object next() { MOFID id = (MOFID)keyIter.next(); try { return get(id); } catch (StorageException ex) { throw new RuntimeStorageException(ex); } } /** Remove is not suppported * @exception UnsupportedOperationException always thrown */ public void remove() { /* an optional operation which we do not support */ throw new UnsupportedOperationException( "Remove is not supported"); } } /** * This is the object returned by BtreeDatabase.keySet(). It uses * the iterator classes to implement the Set interface. */ class Keys extends AbstractSet implements Set { /** return an iterator over all data in the repository * @return iterator */ public Iterator iterator() { synchronized (BtreeDatabase.this) { return new KeyIterator(false); } } /** returns number of objects in repository * @return number of objects in repository */ public int size() { return BtreeDatabase.this.size(); } } /** * This is the object returned by BtreeDatabase.values(). It uses * the iterator classes to implement the Collection interface. Note * that since we don't index on the objects stored in the repository, * only on their MOFIDs, operations like contains will do linear * searches, instantiating objects as they go. */ class Values extends AbstractCollection implements Collection { /** return an iterator over all data in the repository * @return iterator */ public Iterator iterator() { return BtreeDatabase.this.new ValueIterator(); } /** returns number of objects in repository * @return number of objects in repository */ public int size() { return BtreeDatabase.this.size(); } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.