Quadcap Embeddable Database

com/quadcap/sql/file/BlockStore.java

Go to the documentation of this file.
00001 package com.quadcap.sql.file; 00002 00003 /* Copyright 1997 - 2003 Quadcap Software. All rights reserved. 00004 * 00005 * This software is distributed under the Quadcap Free Software License. 00006 * This software may be used or modified for any purpose, personal or 00007 * commercial. Open Source redistributions are permitted. Commercial 00008 * redistribution of larger works derived from, or works which bundle 00009 * this software requires a "Commercial Redistribution License"; see 00010 * http://www.quadcap.com/purchase. 00011 * 00012 * Redistributions qualify as "Open Source" under one of the following terms: 00013 * 00014 * Redistributions are made at no charge beyond the reasonable cost of 00015 * materials and delivery. 00016 * 00017 * Redistributions are accompanied by a copy of the Source Code or by an 00018 * irrevocable offer to provide a copy of the Source Code for up to three 00019 * years at the cost of materials and delivery. Such redistributions 00020 * must allow further use, modification, and redistribution of the Source 00021 * Code under substantially the same terms as this license. 00022 * 00023 * Redistributions of source code must retain the copyright notices as they 00024 * appear in each source code file, these license terms, and the 00025 * disclaimer/limitation of liability set forth as paragraph 6 below. 00026 * 00027 * Redistributions in binary form must reproduce this Copyright Notice, 00028 * these license terms, and the disclaimer/limitation of liability set 00029 * forth as paragraph 6 below, in the documentation and/or other materials 00030 * provided with the distribution. 00031 * 00032 * The Software is provided on an "AS IS" basis. No warranty is 00033 * provided that the Software is free of defects, or fit for a 00034 * particular purpose. 00035 * 00036 * Limitation of Liability. Quadcap Software shall not be liable 00037 * for any damages suffered by the Licensee or any third party resulting 00038 * from use of the Software. 00039 */ 00040 00041 import java.io.File; 00042 import java.io.FileDescriptor; 00043 import java.io.IOException; 00044 import java.io.RandomAccessFile; 00045 00046 import java.util.BitSet; 00047 00048 import com.quadcap.util.Debug; 00049 import com.quadcap.util.Util; 00050 00051 import com.quadcap.sql.Version; 00052 00053 /** 00054 * Below the cache, this class handles the actual I/O to the underlying file. 00055 * Block write operations are logged by this class. 00056 * 00057 * @author Stan Bailes 00058 */ 00059 public class BlockStore { 00060 File file; 00061 FileRandomAccess fra; 00062 boolean readOnly; 00063 Log log; 00064 int blockSize; 00065 BitSet modified = new BitSet(); 00066 Object lock; 00067 boolean encrypted = false; 00068 boolean isTempFile = false; 00069 00070 /** 00071 * Create a new BlockStore object using the specified file and 00072 * blocksize. 00073 * 00074 * @param file the underlying file. 00075 * @param mode "r" for readonly access, otherwise "rw" 00076 * @param blocksize the block size to use when creating the file. 00077 * @param the synchronization object 00078 */ 00079 public BlockStore() { 00080 } 00081 00082 public String getName() { 00083 return file == null ? "null" : file.getName(); 00084 } 00085 00086 public void init(File file, String mode, int blockSize, Object lock) 00087 throws IOException 00088 { 00089 this.file = file; 00090 boolean exists = file.exists(); 00091 RandomAccessFile ra = new RandomAccessFile(file.getAbsolutePath(), mode); 00092 this.fra = new FileRandomAccess(ra, 1024L * 1024L * 1024L * 1024L); 00093 this.readOnly = mode.equalsIgnoreCase("r"); 00094 this.lock = lock; 00095 if (!exists) { 00096 createHeader(blockSize); 00097 } else { 00098 readHeader(); 00099 } 00100 isTempFile = !(file.getPath().endsWith("datafile")); 00101 } 00102 00103 /** 00104 * Specify the log used for recovery operations. 00105 */ 00106 public void setLog(Log log) { this.log = log; } 00107 00108 /** 00109 * Return the logger 00110 */ 00111 public Log getLog() { return log; } 00112 00113 /** 00114 * Return the block size of this store. 00115 */ 00116 public int blockSize() { return blockSize; } 00117 00118 /** 00119 * Return the underlying file 00120 */ 00121 public File getFile() { return file; } 00122 00123 /** 00124 * Read a block into a buffer. If the specified block is beyond the 00125 * current end of file, then grow the file 00126 * 00127 * @param blockNum the number of the block to read. 00128 * @param buf the buffer into which the data is read. 00129 */ 00130 public void read(long blockNum, byte[] buf) throws IOException { 00131 read(blockNum, buf, 0); 00132 } 00133 00134 public void read(long blockNum, byte[] buf, int off) 00135 throws IOException 00136 { 00137 synchronized (lock) { 00138 readCount++; 00139 fra.read(blockNum * blockSize, buf, off, blockSize); 00140 } 00141 } 00142 00143 public static int writeCount = 0; 00144 public static int readCount = 0; 00145 /** 00146 * Write a block from a buffer into the file. 00147 * 00148 * @param blockNum the number of the block to write. 00149 * @param buf the buffer from which the data is written. 00150 * @exception IOException if an I/O error occurs. 00151 */ 00152 public void write(long blockNum, byte[] buf) 00153 throws IOException 00154 { 00155 synchronized (lock) { 00156 writeCount++; 00157 if (readOnly) { 00158 throw new IOException("Attempt to write readonly file: " + 00159 file.getName()); 00160 } 00161 long pos = blockNum * blockSize; 00162 if (log != null && !modified.get((int)blockNum)) { 00163 modified.set((int)blockNum); 00164 if (pos < fra.size()) { 00165 log.saveBlock(blockNum); 00166 } 00167 } 00168 //#ifdef DEBUG 00169 if (Trace.bit(20)) { 00170 Debug.println(toString() + ".write(" + blockNum + "," + 00171 Block.signature(buf) + ") " + Util.stackTrace()); 00172 } 00173 //#endif 00174 fra.write(blockNum * blockSize, buf, 0, buf.length); 00175 } 00176 } 00177 00178 /** 00179 * Restore a block image 00180 */ 00181 public void restore(long blockNum, byte[] buf, int off) 00182 throws IOException 00183 { 00184 synchronized (lock) { 00185 //#ifdef DEBUG 00186 if (Trace.bit(20)) { 00187 Debug.println(toString() + ".restore(" + blockNum + "," + 00188 Block.signature(buf, off, blockSize) + ")"); 00189 } 00190 //#endif 00191 fra.write(blockNum * blockSize, buf, off, blockSize); 00192 } 00193 00194 } 00195 00196 public void clearModified() throws IOException { 00197 modified = new BitSet(); 00198 if (log != null) log.resetBlocks(); 00199 } 00200 00201 00202 public void setLength(long length) throws IOException { 00203 if (readOnly) { 00204 throw new IOException("Attempt to write readonly file: " + 00205 file.getName()); 00206 } 00207 fra.resize(length); 00208 } 00209 00210 public boolean isEncrypted() { 00211 return encrypted; 00212 } 00213 00214 private void readHeader() throws IOException { 00215 byte[] buf = new byte[32]; 00216 fra.read(0, buf, 0, buf.length); 00217 int v = ((buf[0] & 0xff) << 8) + (buf[1] & 0xff); 00218 switch (v) { 00219 case 0x040c: // QED. 00220 encrypted = false; 00221 break; 00222 case 0x0e0c: // Encrypted QED. 00223 encrypted = true; 00224 break; 00225 default: 00226 throw new IOException("BlockFile: bad magic"); 00227 } 00228 // XXX This is hokey. We've tied database versions to the 00229 // XXX product version numbers somehow, so we complain about 00230 // XXX incompatibility based on the wrong version. Need 00231 // XXX BlockFile, Database, versions.... 00232 if (buf[2] != Version.majorVersion) { 00233 throw new IOException("BlockFile: bad version (" + 00234 (int)buf[2] + "." + (int)buf[3] + 00235 " vs " + 00236 Version.majorVersion + "." + 00237 Version.minorVersion + ")"); 00238 } 00239 this.blockSize = ByteUtil.getInt(buf, BlockFile.oBLOCKSIZE); 00240 } 00241 00242 private void createHeader(int blockSize) throws IOException { 00243 this.blockSize = blockSize; 00244 byte[] buf = newHeader(blockSize, 2); 00245 fra.write(0, buf, 0, buf.length); 00246 } 00247 00248 protected final byte[] newHeader(int blockSize, long lastBlock) { 00249 byte[] buf = new byte[blockSize]; 00250 for (int i = 0; i < blockSize; i++) buf[i] = (byte)0; 00251 buf[0] = 0x4; buf[1] = 0xc; // magic 00252 buf[2] = Version.majorVersion; 00253 buf[3] = Version.minorVersion; 00254 ByteUtil.putInt(buf, BlockFile.oBLOCKSIZE, blockSize); 00255 ByteUtil.putLong(buf, BlockFile.oLASTBLOCK, lastBlock); // allocated one block 00256 return buf; 00257 } 00258 00259 public void close() throws IOException { 00260 try { 00261 if (fra != null) fra.close(); 00262 } finally { 00263 fra = null; 00264 } 00265 } 00266 00267 public void flush() throws IOException { 00268 try { 00269 fra.flush(); 00270 readCount = writeCount = 0; 00271 } catch (Throwable t) {} 00272 } 00273 00274 //#ifdef JDK14 00275 public void setKey(com.quadcap.crypto.SymmetricKey key) 00276 throws IOException 00277 { 00278 throw new IOException("Not encrypted"); 00279 } 00280 //#endif 00281 00282 //#ifdef DEBUG 00283 public String toString() { 00284 return "BlockStore(" + file.getPath() + ")"; 00285 } 00286 00287 public static String rw() { 00288 return "(r=" + BlockStore.readCount + 00289 " w=" + BlockStore.writeCount + ")"; 00290 } 00291 00292 //#endif 00293 00294 }