private final ArrayList<InputObjectState> scanLog(final Uid logName, final String typeName) throws ObjectStoreException { /* * Make sure no new entries can be created while we scan. */ synchronized (_lock) { try { String fname = genPathName(logName, typeName, StateStatus.OS_COMMITTED); File fd = openAndLock(fname, FileLock.F_WRLCK, true); RandomAccessFile iFile = new RandomAccessFile(fd, FILE_MODE); // iFile.getChannel().lock(); try { /* * Create a list of ObjectState entries. */ ArrayList<InputObjectState> objectStates = new ArrayList<InputObjectState>(); iFile.seek(0); // make sure we're at the start while (iFile.getFilePointer() < iFile.length()) { byte[] buff = new byte[_redzone.length]; iFile.read(buff); if (!redzoneProtected(buff)) { // end break; /* * TODO add an end-of-log entry and check for that. Currently just assume * that no RZ means end, rather than corruption. */ } else { int uidSize = iFile.readInt(); byte[] uidString = new byte[uidSize]; iFile.read(uidString); Uid txId = new Uid(new String(uidString)); int imageSize = iFile.readInt(); byte[] imageState = new byte[imageSize]; iFile.read(imageState); try { InputObjectState state = new InputObjectState(txId, "", imageState); objectStates.add(state); } catch (final Exception ex) { ex.printStackTrace(); throw new ObjectStoreException(ex.toString(), ex); } } } unlockAndClose(fd, iFile); iFile = null; /* * At this stage we now have a list of ObjectState entries. * Now we need to go through and prune the list. This is * complicated by the fact that there can be 1.. entries for * a specific transaction since we continually update the * log as we drive recovery. If an entry hasn't been deleted * then we will keep the latest one we find. */ /* * First search for those entries that have been deleted. */ ArrayList<InputObjectState> deletedLogs = new ArrayList<InputObjectState>(); for (int i = 0; i < objectStates.size(); i++) { InputObjectState curr = objectStates.get(i); try { if (Arrays.equals(curr.unpackBytes(), _removedState)) { deletedLogs.add(curr); } else curr.reread(); // don't forget to reset the read pointer! } catch (final Exception ex) { // if not a delete record then the first entry won't // be an the defined byte array. curr.reread(); // don't forget to reset the read pointer! } } if (deletedLogs.size() > 0) { /* * make sure we remove them from the first list to save time. */ objectStates.removeAll(deletedLogs); deleteEntries(objectStates, deletedLogs); /* * At this stage we should only have entries that refer * to in-flight transactions. Go through the list and * remove N-1 references for each transaction id. */ pruneEntries(objectStates); /* * Now return the list of committed entries. */ return objectStates; } else return objectStates; } finally { if (iFile != null) unlockAndClose(fd, iFile); } } catch (final ObjectStoreException ex) { ex.printStackTrace(); throw ex; } catch (final Exception ex) { ex.printStackTrace(); throw new ObjectStoreException(ex.toString(), ex); } } }
private final boolean truncateLog(final LogInstance log, boolean force) throws ObjectStoreException { boolean delete = false; synchronized (_lock) { File fd = new File(genPathName(log.getName(), log.getTypeName(), StateStatus.OS_COMMITTED)); try { /* * Create a list of ObjectState entries. */ ArrayList<InputObjectState> objectStates = scanLog(log.getName(), log.getTypeName()); /* * At this stage we should now have a list of unique * entries. Write them back to the log. Do this * atomically! If the list is empty then delete the * file! */ if ((objectStates != null) && (objectStates.size() > 0)) { /* * If we are terminating then we can truncate the log to the * real size needed to contain the existing entries since we * will not use it again within another VM except for * recovery purposes. */ String fname = genPathName(log.getName(), log.getTypeName(), StateStatus.OS_UNCOMMITTED); File fd2 = openAndLock(fname, FileLock.F_WRLCK, true); RandomAccessFile oFile = new RandomAccessFile(fd2, FILE_MODE); int size = 0; oFile.setLength(_maxFileSize); for (int i = 0; i < objectStates.size(); i++) { byte[] uidString = objectStates.get(i).stateUid().stringForm().getBytes(); int buffSize = _redzone.length + uidString.length + objectStates.get(i).buffer().length + 8; java.nio.ByteBuffer buff = java.nio.ByteBuffer.allocate(buffSize); size += buffSize; try { buff.put(_redzone); buff.putInt(uidString.length); buff.put(uidString); buff.putInt(objectStates.get(i).buffer().length); buff.put(objectStates.get(i).buffer(), 0, objectStates.get(i).buffer().length); } catch (final Exception ex) { ex.printStackTrace(); // TODO log fd2.delete(); unlockAndClose(fd2, oFile); throw new ObjectStoreException(ex.toString(), ex); } } try { if (force) { oFile.setLength(size); log.freeze(); } fd2.renameTo(fd); } catch (final Exception ex) { ex.printStackTrace(); // TODO log throw new ObjectStoreException(ex.toString(), ex); } finally { unlockAndClose(fd2, oFile); } } else { /* * Delete the log if there are no states in it. We could * keep the file around and reuse it, but the advantage of * this is small compared to having to cope with reusing old * log instances. */ fd.delete(); /* * Remember to remove the information from the memory cache. */ delete = true; } } catch (final ObjectStoreException ex) { ex.printStackTrace(); throw ex; } catch (final Exception ex) { ex.printStackTrace(); throw new ObjectStoreException(ex.toString(), ex); } } return delete; }