You are viewing a plain text version of this content. The canonical link for it is here.
Posted to kato-commits@incubator.apache.org by mo...@apache.org on 2009/10/28 15:15:31 UTC

svn commit: r830571 - in /incubator/kato/trunk/org.apache.kato/kato.cjvmti/src: main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java

Author: monteith
Date: Wed Oct 28 15:15:29 2009
New Revision: 830571

URL: http://svn.apache.org/viewvc?rev=830571&view=rev
Log:
Added some documentation and another test. That should be all for now.

Modified:
    incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java
    incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java

Modified: incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java
URL: http://svn.apache.org/viewvc/incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java?rev=830571&r1=830570&r2=830571&view=diff
==============================================================================
--- incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java (original)
+++ incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/main/java/org/apache/kato/jvmti/util/CachedRandomAccessFile.java Wed Oct 28 15:15:29 2009
@@ -20,27 +20,73 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 
-public class CachedRandomAccessFile extends RandomAccessFile {
+/*
+ * This class could be modified to implement the write methods.
+ * To do so the write methods could be modified to behave much like
+ * the read methods. However, the cache would have to contain more
+ * than just bytes such that they contain a "modified" boolean value
+ * so the array can be written back to disk when they are pushed out
+ * of the cache. Consideration will also need to be paid to length()
+ * , setLength() and writing new cache blocks. 
+ * 
+ * CachedLinkHashMap.removeEldestEntry() is passed the entry to remove.
+ * This could test for modification and then write it out if necessary.
+ * 
+ * The constructors must be modified to open a writable file, but
+ * it's not necessary for it to be 
+ * 
+ * A flush method can be added to allow the cache to be written out
+ * and the cache blocks to be marked as unmodified - class can
+ * be modified to implement Flushable.
+ * 
+ * 
+ * The close method will have to write out the remaining dirty
+ * pages before closing.
+ * 
+ * 
+ */
+
+/**
+ * <p>
+ * A read-only RandomAccessFile with caching.
+ * Implements an LRU cache to store blocks.
+ * Blocks are implemented as byte arrays.
+ * </p><p>
+ * Only overrides the read methods that must be implemented.
+ * The final read methods call the ones we have implemented here,
+ * {@link readUTF()}, for instance.
+ * </p><p>
+ * {@link getFD()} and {@link getChannel()} won't benefit
+ * from this class's caching and won't report the same
+ * file position that would be returned by {@link getFilePointer()}.
+ * Also, if the file pointer is modified by FileChannel, 
+ * </p>
+ * 
+ */
+final public class CachedRandomAccessFile extends RandomAccessFile {
 	// The default block size, in bits. 4096 bytes.
 	final static private int DEFAULT_BITS=12;
-	final static private int DEFAULT_SIZE=32;// in MB
-	int cacheFactor = 10; // cache 10th of a file
-	int cacheBits;  // Number of bits representing cache.
-	int blockSize; // block size.
-	long cacheUpperMask;
-	long cacheLowerMask;
-	long length;
-	int numCacheEntries;
-	long position=0l;
-	
-	CacheLinkedHashMap<Long,byte[]> cacheMap;
-	
-	/**
-	 * Returns the block of bytes at a particular address.
-	 * 
-	 * @param pos
-	 * @return
-	 * @throws IOException
+	final static private int DEFAULT_SIZE=16;// in MB
+	private int cacheBits;  // Number of bits representing cache.
+	private int blockSize; // block size.
+	private long cacheUpperMask; // For extracting cache tag
+	private long cacheLowerMask; // For extracting position in block
+	private long length; // Length of the file
+	private int numCacheEntries; // Number of cache entries to maintain
+	private long position=0l; // Position in file.
+	
+	// The map from file locations to byte arrays.
+	private CacheLinkedHashMap<Long,byte[]> cacheMap;
+	
+	/**
+	 * Returns the block of bytes at a particular address. 
+	 * The pos parameter will be within the block, not necessarily
+	 * the beginning of the block.
+	 * If necessary the block will be loaded from disk.
+	 * 
+	 * @param pos file pointer to return appropriate block for.
+	 * @return byte[] containing blocks
+	 * @throws IOException thrown if underlying read fails.
 	 */
 	byte[] getBlock(long pos) throws IOException {
 		long tag = (pos & cacheUpperMask) >> cacheBits;
@@ -55,8 +101,11 @@
 			}
 			
 			if (blockSize > length) {
-				block = new byte[(int)length];
+				// a file could be less than the blocksize in length.
+				block = new byte[(int)length];				
 			} else if (pos > (length-blockSize)) {
+				// The last block in the file. File sizes are unlikely to be
+				// a multiple of the cache block size.
 				block = new byte[(int)(length-pos)];
 			} else {
 				block = new byte[blockSize];
@@ -71,10 +120,11 @@
 	}
 	
 	/**
+	 * Initialises the field variables and the cache.
 	 * 
 	 * @param cacheBits size of cache block in powers of 2
 	 * @param cacheSize size of cache in megabytes
-	 * @throws IOException 
+	 * @throws IOException if length() fails.
 	 */
 	private void init(int cacheBits, int cacheSize) throws IOException {
 		this.cacheBits = cacheBits;
@@ -89,30 +139,85 @@
 		this.cacheMap = new CacheLinkedHashMap<Long, byte[]>(numCacheEntries);
 	}
 	
+	/**
+	 * Creates a CachedRandomAccessFile open against the
+	 * past File. 
+	 * 
+	 * @param file File to open.
+	 * @throws IOException if file is not found.
+	 */
 	public CachedRandomAccessFile(File file)
 	throws IOException {
 		this(file, DEFAULT_BITS, DEFAULT_SIZE);
 	}
 
+	/**
+	 * Creates a CachedRandomAccessFile open against the
+	 * past file name.
+	 * 
+	 * @param name Name of file to open.
+	 * @throws IOException if file is not found.
+	 */
 	public CachedRandomAccessFile(String name)
 	throws IOException {
 		this(name, DEFAULT_BITS, DEFAULT_SIZE);
 	}
 
+	/**
+	 * <p>Creates a CachedRandomAccessFile open against the
+	 * past File.
+	 * </p><p>
+	 * The cacheBits parameter is the number of bits that are
+	 * used to address entries within cache blocks. The default
+	 * value is 12, which equates to 4096 bytes per block. This
+	 * is the amount of data read at once and stored within the cache.
+	 * </p><p>
+	 * The cacheSize is the maximum amount of memory occupied by the cache.
+	 * This value is in megabytes. The default is 16, which is 16 megabytes.
+	 * </p>
+	 * 
+	 * @param file File to open.
+	 * @param cacheBits Number of bits representing cache block size
+	 * @param cacheSize Cache size in megabytes.
+	 * @throws IOException if file is not found.
+	 */
 	public CachedRandomAccessFile(File file, int cacheBits, int cacheSize)
 	throws IOException {
 		super(file, "r");
 		
 		init(cacheBits, cacheSize);
 	}
-
+	/**
+	 * <p>Creates a CachedRandomAccessFile open against the
+	 * past file name.
+	 * </p><p>
+	 * The cacheBits parameter is the number of bits that are
+	 * used to address entries within cache blocks. The default
+	 * value is 12, which equates to 4096 bytes per block. This
+	 * is the amount of data read at once and stored within the cache.
+	 * </p><p>
+	 * The cacheSize is the maximum amount of memory occupied by the cache.
+	 * This value is in megabytes. The default is 16, which is 16 megabytes.
+	 * </p>
+	 * 
+	 * @param name Name fo file to open.
+	 * @param cacheBits Number of bits representing cache block size
+	 * @param cacheSize Cache size in megabytes.
+	 * @throws IOException if file is not found.
+	 */
 	public CachedRandomAccessFile(String name, int cacheBits, int cacheSize)
 	throws IOException {
 		super(name, "r");
 		
 		init(cacheBits, cacheSize);
 	}
-	
+
+	/**
+	 * Reads a byte value. 
+	 * 
+	 * @return unsigned byte value, or -1 if beyond end of file.
+	 * @throws IOException if underlying read fails.
+	 */
 	@Override
 	public int read() throws IOException {
 		// At end of file.
@@ -200,13 +305,25 @@
 	}
 	
 	
+	/**
+	 * Returns the position within the file.
+	 * The underlying RandomAccessFile file pointer may be different
+	 * from this value as seeks are carried out only when cache blocks
+	 * are read from disk.
+	 * 
+	 */
 	@Override
 	public long getFilePointer() throws IOException {
 		return position;
 	}
 	
 	/**
+	 * Repositions the file-pointer offset to where the
+	 * next read will occur. This does not affect the underlying
+	 * RandomAccessFile unless a cache block has to be read in.
 	 * 
+	 * 
+	 * @throws IOException is throws if pos is < 0. 
 	 */
 	@Override
 	public void seek(long pos) throws IOException {
@@ -217,7 +334,10 @@
 	}
 
 	/**
-	 * Skips over the number
+	 * Skips over some bytes without reading them.
+	 * This doesn't affect the RandomAccessFile
+	 * position. 
+	 * 
 	 * @param n number of bytes to skip over.
 	 * @returns number of bytes skipped
 	 */
@@ -238,6 +358,15 @@
 		return n;
 	}
 	
+	/**
+	 * Closes the open file and empties the cache. 
+	 * As the cache is empty after this, future accesses
+	 * will try to load the cache blocks from disk, and so
+	 * will fail. Operations affecting the file position will
+	 * continue to work without exceptions being thrown.
+	 * 
+	 * @throws IOException if underlying close throws IOException.
+	 */
 	@Override
 	public void close() throws IOException {
 		super.close();
@@ -249,43 +378,70 @@
 	
 	// Don't allow any write operations
 	
+	/**
+	 * No write operations are supported, so an IOException is always
+	 * thrown.
+	 * 
+	 * @throws IOException is thrown unconditionally. 
+	 */
 	@Override
 	public void write(int b) throws IOException {
 		throw new IOException("CachedRandomAccessFile is not writable");
 	}
 
+	/**
+	 * No write operations are supported, so an IOException is always
+	 * thrown.
+	 * 
+	 * @throws IOException is thrown unconditionally. 
+	 */
 	@Override
 	public void write(byte[] b) throws IOException {
 		throw new IOException("CachedRandomAccessFile is not writable");
 	}
 
+	/**
+	 * No write operations are supported, so an IOException is always
+	 * thrown.
+	 * 
+	 * @throws IOException is thrown unconditionally.
+	 */
 	@Override
 	public void write(byte[] b, int off, int len) throws IOException {
 		throw new IOException("CachedRandomAccessFile is not writable");
 	}	
 	
+	/**
+	 * No write operations are supported, so an IOException is always
+	 * thrown.
+	 * 
+	 * @throws IOException is thrown unconditionally.
+	 */
 	@Override
 	public void setLength(long newLength) throws IOException {
 		throw new IOException("CachedRandomAccessFile is not writable");
 	}
 
 	/**
-	 * LinkedHashMap for our caching purposes.  
-	 * Ensures the cache uses access ordering.
+	 * LinkedHashMap for our caching purposes.
+	 * Used as a LRU cache, holding up to a fixed
+	 * number of cache entries.  
 	 *	
-	 *
-	 * @param <K>
-	 * @param <V>
+	 * @param <K> Key type
+	 * @param <V> Value type
 	 */
-	private static class CacheLinkedHashMap<K,V> extends LinkedHashMap<K,V> {
+	@SuppressWarnings("serial")
+	private final static class CacheLinkedHashMap<K,V> extends LinkedHashMap<K,V> {
 		private int maxCacheEntries;
 		
 		/**
+		 * Creates a LinkedHashMap with access ordering.
+		 * The oldest entry is the oldest entry to be put or
+		 * got.
+		 * Takes the number of entries to cache.
 		 * 
 		 * 
-		 * 
-		 * @param initialCapacity
-		 * @param maxCacheEntries
+		 * @param maxCacheEntries maximum number of entries to hold.
 		 */
 		public CacheLinkedHashMap(int maxCacheEntries) {
 			super(maxCacheEntries, 0.75f, true);
@@ -298,7 +454,7 @@
 		 * the cache size. 
 		 * 
 		 * @return true if cache entry is to be removed.
-		 * @param entru
+		 * @param entry oldest entry to handle.
 		 */
 		@Override
 		protected boolean removeEldestEntry(Map.Entry<K,V> entry) {

Modified: incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java
URL: http://svn.apache.org/viewvc/incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java?rev=830571&r1=830570&r2=830571&view=diff
==============================================================================
--- incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java (original)
+++ incubator/kato/trunk/org.apache.kato/kato.cjvmti/src/test/java/org/apache/kato/jvmti/util/CachedRandomAccessFileTest.java Wed Oct 28 15:15:29 2009
@@ -101,6 +101,29 @@
 	}
 	
 	/**
+	 * Tests that reading a closed file fails with an IOException.
+	 * 
+	 * @throws Exception
+	 */
+	public void testClose() throws Exception {
+		craf = new CachedRandomAccessFile(file, 2, 32);
+		byte bytes[] = new byte[FILE_SIZE];
+		
+		craf.read(bytes);
+				
+		craf.close();
+
+		craf.seek(0);
+		
+		try{
+			craf.read(bytes);
+			fail("Failed to throw IOException on closed file.");
+		} catch (IOException e) {
+			// expected
+		}
+
+	}
+	/**
 	 * Check that when the file is read into an offset
 	 * within the array, that only the bytes in the file
 	 * are read.