1 24 package org.objectweb.jalisto.se.storage.raf; 25 26 import org.objectweb.jalisto.se.impl.trace.Trace; 27 import org.objectweb.jalisto.se.impl.util.JalistoUtils; 28 29 import java.io.File ; 30 import java.io.IOException ; 31 import java.io.RandomAccessFile ; 32 import java.util.*; 33 34 public class RecordsFile { 35 protected static final int FILE_HEADERS_REGION_LENGTH = 16; 37 38 protected static final int RECORD_HEADER_LENGTH = 16; 40 41 protected static final long NUM_RECORDS_HEADER_LOCATION = 0; 43 44 protected static final long DATA_START_HEADER_LOCATION = 4; 46 47 protected double additionalSpace; 48 49 protected int indexEntryLength; 51 52 protected int maxKeyLength; 54 55 protected long dataStartPtr; 57 58 protected Trace trace; 59 60 protected RandomAccessFile file; 62 63 67 protected HashMap memIndex; 68 69 70 76 public RecordsFile(String dbPath) 77 throws IOException , RecordsFileException { 78 File f = new File (dbPath); 79 if (f.exists()) { 80 throw new RecordsFileException("Database already exits: " + dbPath); 81 } 82 file = new RandomAccessFile (f, "rw"); 83 } 84 85 90 public RecordsFile(String dbPath, String accessFlags) 91 throws IOException , RecordsFileException { 92 File f = new File (dbPath); 93 if (!f.exists()) { 94 throw new RecordsFileException("Database not found: " + dbPath); 95 } 96 file = new RandomAccessFile (f, accessFlags); 97 } 98 99 public void initNewStorage(int initialSize, int maxKeyLength, int additionalSpace) 100 throws IOException , RecordsFileException { 101 this.maxKeyLength = maxKeyLength; this.additionalSpace = (((double) additionalSpace) / 100) + 1; 103 this.indexEntryLength = maxKeyLength + RECORD_HEADER_LENGTH; 104 this.dataStartPtr = indexPositionToKeyFp(initialSize); setFileLength(dataStartPtr); 106 writeNumRecordsHeader(0); 107 writeDataStartPtrHeader(dataStartPtr); 108 memIndex = new HashMap(initialSize); 109 } 110 111 public void initExistingStorage(int maxKeyLength, int additionalSpace) 112 throws IOException , RecordsFileException { 113 this.maxKeyLength = maxKeyLength; this.additionalSpace = (((double) additionalSpace) / 100) + 1; 115 this.indexEntryLength = maxKeyLength + RECORD_HEADER_LENGTH; 116 this.dataStartPtr = readDataStartHeader(); 117 readHeadersInMemIndex(); 118 } 119 120 public synchronized void close() throws IOException , RecordsFileException { 121 try { 122 file.close(); 123 } finally { 124 memIndex.clear(); 125 memIndex = null; 126 file = null; 127 } 128 } 129 130 133 134 public synchronized void insertRecord(RecordWriter rw) throws RecordsFileException, IOException { 135 String key = rw.getKey(); 136 if (recordExists(key)) { 137 throw new RecordsFileException("Key exists: " + key); 138 } 139 insureIndexSpace(getNumRecords() + 1); 140 int length = (new Double (rw.getDataLength() * additionalSpace)).intValue(); 141 RecordHeader newRecord = allocateRecord(key, length); 142 writeRecordData(newRecord, rw); 143 addEntryToIndex(key, newRecord, getNumRecords()); 144 } 145 146 public synchronized RecordReader readRecord(String key) throws RecordsFileException, IOException { 147 byte[] data = readRecordData(key); 148 return new RecordReader(key, data); 149 } 150 151 155 public synchronized void updateRecord(RecordWriter rw) 156 throws RecordsFileException, IOException { 157 RecordHeader header = keyToRecordHeader(rw.getKey()); 158 159 if (rw.getDataLength() > header.getDataCapacity()) { 160 deleteRecord(rw.getKey()); 161 insertRecord(rw); 162 } else { 163 writeRecordData(header, rw); 164 writeRecordHeaderToIndex(header); 165 } 166 } 167 168 public synchronized void deleteRecord(String key) throws RecordsFileException, IOException { 169 RecordHeader delRec = keyToRecordHeader(key); 170 int currentNumRecords = getNumRecords(); 171 172 if (getFileLength() == (delRec.getDataPointer() + delRec.getDataCapacity())) { 173 setFileLength(delRec.getDataPointer()); 175 } else { 176 RecordHeader previous = getRecordAt(delRec.getDataPointer() - 1); 177 178 if (previous != null) { 179 previous.setDataCapacity(previous.getDataCapacity() + delRec.getDataCapacity()); 181 writeRecordHeaderToIndex(previous); 182 } else { 183 RecordHeader secondRecord = getRecordAt(delRec.getDataPointer() + (long) delRec.getDataCapacity()); 186 byte[] data = readRecordData(secondRecord); 187 secondRecord.setDataPointer(delRec.getDataPointer()); 188 secondRecord.setDataCapacity(secondRecord.getDataCapacity() + delRec.getDataCapacity()); 189 writeRecordData(secondRecord, data); 190 writeRecordHeaderToIndex(secondRecord); 191 } 192 } 193 deleteEntryFromIndex(key, delRec, currentNumRecords); 194 } 195 196 197 protected void setFileLength(long l) throws IOException { 198 file.setLength(l); 199 } 200 201 protected long getFileLength() throws IOException { 202 return file.length(); 203 } 204 205 208 protected void addEntryToIndex(String key, RecordHeader newRecord, int currentNumRecords) 209 throws IOException , RecordsFileException { 210 DbByteArrayOutputStream temp = DbByteArrayOutputStream.getInstance(); 211 temp.getOutput().writeUTF(key); 212 213 if (temp.size() > maxKeyLength) { 214 throw new RecordsFileException("Key " + key + " of size " + temp.size() + 215 " is larger than permitted size of " + 216 maxKeyLength + " bytes"); 217 } 218 219 writeOutputStream(indexPositionToKeyFp(currentNumRecords), temp); 220 temp.reset(); 221 writeRecordHeader(indexPositionToRecordHeaderFp(currentNumRecords), newRecord); 222 newRecord.setIndexPosition(currentNumRecords); 223 writeNumRecordsHeader(currentNumRecords + 1); 224 225 synchronized (memIndex) { 226 memIndex.put(key, newRecord); 227 } 228 } 229 230 234 protected void deleteEntryFromIndex(String key, RecordHeader header, int currentNumRecords) 235 throws IOException , RecordsFileException { 236 if (header.getIndexPosition() != (currentNumRecords - 1)) { 237 String lastKey = readKeyFromIndex(currentNumRecords - 1); 238 RecordHeader last = keyToRecordHeader(lastKey); 239 last.setIndexPosition(header.getIndexPosition()); 240 writeUTF(indexPositionToKeyFp(last.getIndexPosition()), lastKey); 241 writeRecordHeader(indexPositionToRecordHeaderFp(last.getIndexPosition()), last); 242 } 243 writeNumRecordsHeader(currentNumRecords - 1); 244 synchronized (memIndex) { 245 memIndex.remove(key); 246 } 247 } 248 249 public Collection getKeysStartingWith(String filter) { 250 Collection result = new ArrayList(); 251 synchronized (memIndex) { 252 Iterator keys = memIndex.keySet().iterator(); 253 while (keys.hasNext()) { 254 String key = (String ) keys.next(); 255 if (key.startsWith(filter)) { 256 result.add(key); 257 } 258 } 259 } 260 return result; 261 } 262 263 264 protected void insureIndexSpace(int requiredNumRecords) 267 throws RecordsFileException, IOException { 268 int originalFirstDataCapacity; 269 int currentNumRecords = getNumRecords(); 270 long endIndexPtr = indexPositionToKeyFp(requiredNumRecords); 271 272 if ((endIndexPtr > getFileLength()) && (currentNumRecords == 0)) { 273 setFileLength(endIndexPtr); 274 dataStartPtr = endIndexPtr; 275 writeDataStartPtrHeader(dataStartPtr); 276 277 return; 278 } 279 280 while (endIndexPtr > dataStartPtr) { 281 RecordHeader first = getRecordAt(dataStartPtr); 282 byte[] data = readRecordData(first); 283 first.setDataPointer(getFileLength()); 284 285 originalFirstDataCapacity = first.getDataCapacity(); 289 first.setDataCapacity(data.length); 290 setFileLength(first.getDataPointer() + data.length); 291 writeRecordData(first, data); 292 writeRecordHeaderToIndex(first); 293 dataStartPtr += originalFirstDataCapacity; 294 writeDataStartPtrHeader(dataStartPtr); 295 } 296 } 297 298 302 protected long indexPositionToKeyFp(int pos) { 303 return FILE_HEADERS_REGION_LENGTH + (indexEntryLength * pos); 304 } 305 306 protected long readDataStartHeader() throws IOException { 307 file.seek(DATA_START_HEADER_LOCATION); 308 return file.readLong(); 309 } 310 311 protected int readNumRecordsHeader() throws IOException { 312 file.seek(NUM_RECORDS_HEADER_LOCATION); 313 return file.readInt(); 314 } 315 316 protected byte[] readRecordData(String key) throws IOException , RecordsFileException { 317 return readRecordData(keyToRecordHeader(key)); 318 } 319 320 protected byte[] readRecordData(RecordHeader header) throws IOException { 321 byte[] buf = new byte[header.getDataCount()]; 322 file.seek(header.getDataPointer()); 323 file.readFully(buf); 324 return buf; 325 } 326 327 protected void writeDataStartPtrHeader(long dataStartPtr) throws IOException { 328 writeLong(DATA_START_HEADER_LOCATION, dataStartPtr); 329 } 330 331 protected void writeNumRecordsHeader(int numRecords) throws IOException { 332 writeInt(NUM_RECORDS_HEADER_LOCATION, numRecords); 333 } 334 335 340 protected void writeRecordData(RecordHeader header, RecordWriter rw) throws IOException , RecordsFileException { 341 if (rw.getDataLength() > header.getDataCapacity()) { 342 throw new RecordsFileException("Record data does not fit"); 343 } 344 header.setDataCount(rw.getDataLength()); 345 writeRecordWriter(header, rw); 346 } 347 348 353 protected void writeRecordData(RecordHeader header, byte[] data) 354 throws IOException , RecordsFileException { 355 if (data.length > header.getDataCapacity()) { 356 throw new RecordsFileException("Record data does not fit"); 357 } 358 header.setDataCount(data.length); 359 writeByteArray(header.getDataPointer(), data); 360 } 361 362 protected void writeRecordHeaderToIndex(RecordHeader header) throws IOException { 363 writeRecordHeader(indexPositionToRecordHeaderFp(header.getIndexPosition()), header); 364 synchronized (memIndex) { 365 memIndex.put(readKeyFromIndex(header.getIndexPosition()), header); 366 } 367 } 368 369 373 long indexPositionToRecordHeaderFp(int pos) { 374 return indexPositionToKeyFp(pos) + maxKeyLength; 375 } 376 377 380 protected String readKeyFromIndex(int position) throws IOException { 381 file.seek(indexPositionToKeyFp(position)); 382 return file.readUTF(); 383 } 384 385 388 protected RecordHeader readRecordHeaderFromIndex(int position) throws IOException { 389 file.seek(indexPositionToRecordHeaderFp(position)); 390 return RecordHeader.readHeader(file); 391 } 392 393 394 public synchronized void reorganize() throws IOException , RecordsFileException { 395 long length = file.length(); 396 long moveAddress = dataStartPtr; 397 long nextTargetAddress = dataStartPtr; 398 while (length > nextTargetAddress) { 399 RecordHeader header = getRecordAt(nextTargetAddress); 400 nextTargetAddress = header.getDataPointer() + header.getDataCapacity(); 401 moveAddress = moveDataBack(header, moveAddress); 402 } 403 setFileLength(moveAddress); 404 } 405 406 private synchronized long moveDataBack(RecordHeader header, long moveAddress) throws IOException , RecordsFileException { 408 if (header.getDataPointer() > moveAddress) { 409 byte[] buf = readRecordData(header); 410 long maxAddress = header.getDataPointer() + header.getDataCapacity(); 411 int size = (new Double (buf.length * additionalSpace)).intValue(); 412 413 if ((moveAddress + size) > maxAddress) { 414 size = (int) (maxAddress - moveAddress); 415 } 416 417 RecordHeader newHeader = new RecordHeader(moveAddress, size); 418 writeRecordData(newHeader, buf); 419 newHeader.setIndexPosition(header.getIndexPosition()); 420 writeRecordHeaderToIndex(newHeader); 421 return newHeader.getDataPointer() + newHeader.getDataCapacity(); 422 423 } else if (header.getDataPointer() == moveAddress) { 425 int size = (new Double (header.getDataCount() * additionalSpace)).intValue(); 426 if (size > header.getDataCapacity()) { 427 size = header.getDataCapacity(); 428 } 429 header.setDataCapacity(size); 430 writeRecordHeaderToIndex(header); 431 return (header.getDataPointer() + header.getDataCapacity()); 432 } else { 433 throw new RecordsFileException("moveDataBack address error"); 434 } 435 } 436 437 public void setTrace(Trace trace) { 438 this.trace = trace; 439 } 440 441 442 445 446 protected void writeByteArray(long position, byte[] datas) throws IOException { 447 file.seek(position); 448 file.write(datas); 449 } 450 451 protected void writeOutputStream(long position, DbByteArrayOutputStream outputStream) throws IOException { 452 file.seek(position); 453 outputStream.writeTo(file); 454 } 455 456 protected void writeRecordWriter(RecordHeader header, RecordWriter recordWriter) throws IOException { 457 file.seek(header.getDataPointer()); 458 recordWriter.writeTo(file); 459 } 460 461 protected void writeRecordHeader(long position, RecordHeader recordHeader) throws IOException { 462 file.seek(position); 463 recordHeader.write(file); 464 } 465 466 protected void writeInt(long position, int i) throws IOException { 467 file.seek(position); 468 file.writeInt(i); 469 } 470 471 protected void writeLong(long position, long l) throws IOException { 472 file.seek(position); 473 file.writeLong(l); 474 } 475 476 protected void writeUTF(long position, String s) throws IOException { 477 file.seek(position); 478 file.writeUTF(s); 479 } 480 481 482 485 486 487 public synchronized int getNumRecords() { 488 return memIndex.size(); 489 } 490 491 public synchronized Iterator enumerateKeys() { 492 return memIndex.keySet().iterator(); 493 } 494 495 public synchronized boolean recordExists(String key) { 496 return memIndex.containsKey(key); 497 } 498 499 504 protected RecordHeader getRecordAt(long targetFp) { 505 Iterator elements = memIndex.values().iterator(); 506 while (elements.hasNext()) { 507 RecordHeader next = (RecordHeader) elements.next(); 508 if ((targetFp >= next.getDataPointer()) && 509 (targetFp < (next.getDataPointer() + 510 (long) next.getDataCapacity()))) { 511 return next; 512 } 513 } 514 return null; 515 } 516 517 521 protected RecordHeader allocateRecord(String key, int dataLength) 522 throws RecordsFileException, IOException { 523 RecordHeader newRecord = null; 525 Iterator elements = memIndex.values().iterator(); 526 while (elements.hasNext()) { 527 RecordHeader next = (RecordHeader) elements.next(); 528 if (dataLength <= next.getFreeSpace()) { 529 newRecord = next.split(); 530 writeRecordHeaderToIndex(next); 531 break; 532 } 533 } 534 if (newRecord == null) { 535 long fp = getFileLength(); 537 setFileLength(fp + dataLength); 538 newRecord = new RecordHeader(fp, dataLength); 539 } 540 return newRecord; 541 } 542 543 546 protected RecordHeader keyToRecordHeader(String key) throws RecordsFileException { 547 RecordHeader h = (RecordHeader) memIndex.get(key); 548 if (h == null) { 549 throw new RecordsFileException("Key not found: " + key); 550 } 551 return h; 552 } 553 554 protected void readHeadersInMemIndex() throws IOException { 555 int numRecords = readNumRecordsHeader(); 556 memIndex = new HashMap(numRecords); 557 558 synchronized (memIndex) { 559 for (int i = 0; i < numRecords; i++) { 560 String key = readKeyFromIndex(i); 561 RecordHeader header = readRecordHeaderFromIndex(i); 562 header.setIndexPosition(i); 563 memIndex.put(key, header); 564 } 565 } 566 } 567 568 571 572 public boolean equals(Object object) { 573 RecordsFile candidate; 574 try { 575 candidate = (RecordsFile) object; 576 } catch (ClassCastException cce) { 577 System.out.println("RecordsFile equals : candidate is not a RecordsFile instance"); 578 return false; 579 } 580 581 if (memIndex.size() != candidate.memIndex.size()) { 582 System.out.println("RecordsFile equals : not the same number of key"); 583 return false; 584 } 585 586 Iterator keys = memIndex.keySet().iterator(); 587 while (keys.hasNext()) { 588 String key = (String ) keys.next(); 589 if (!candidate.memIndex.containsKey(key)) { 590 System.out.println("RecordsFile equals : one contains a key, other don't"); 591 return false; 592 } 593 RecordHeader rh = (RecordHeader) memIndex.get(key); 594 RecordHeader crh = (RecordHeader) candidate.memIndex.get(key); 595 if (!rh.equals(crh)) { 596 System.out.println("RecordsFile equals : recordHeader not equal for key " + key); 597 System.out.println(rh.toString()); 598 System.out.println(crh.toString()); 599 return false; 600 } 601 try { 602 if (!JalistoUtils.areEquals(readRecordData(rh), candidate.readRecordData(crh))) { 603 System.out.println("RecordsFile equals : datas not equals for key " + key); 604 return false; 605 } 606 } catch (IOException ioe) { 607 ioe.printStackTrace(); 608 return false; 609 } 610 } 611 612 return true; 613 } 614 } 615 | Popular Tags |