package org.prevayler.implementation; import org.prevayler.foundation.FileManager; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; public class PrevaylerDirectory { private static final int DIGITS_IN_FILENAME=19; private static final String SNAPSHOT_SUFFIX_PATTERN="[a-zA-Z0-9]*[Ss]napshot"; private static final String SNAPSHOT_FILENAME_PATTERN="\\d{" + DIGITS_IN_FILENAME + "}\\."+ SNAPSHOT_SUFFIX_PATTERN; private static final String JOURNAL_SUFFIX_PATTERN="[a-zA-Z0-9]*[Jj]ournal"; private static final String JOURNAL_FILENAME_PATTERN="\\d{" + DIGITS_IN_FILENAME + "}\\."+ JOURNAL_SUFFIX_PATTERN; private File _directory; public PrevaylerDirectory( String directory){ this(new File(directory)); } public PrevaylerDirectory( File directory){ _directory=directory; } public void produceDirectory() throws IOException { FileManager.produceDirectory(_directory); } public static void checkValidSnapshotSuffix( String suffix){ if (!suffix.matches(SNAPSHOT_SUFFIX_PATTERN)) { throw new IllegalArgumentException("Snapshot filename suffix must match /" + SNAPSHOT_SUFFIX_PATTERN + "/, but '"+ suffix+ "' does not"); } } public static void checkValidJournalSuffix( String suffix){ if (!suffix.matches(JOURNAL_SUFFIX_PATTERN)) { throw new IllegalArgumentException("Journal filename suffix must match /" + JOURNAL_SUFFIX_PATTERN + "/, but '"+ suffix+ "' does not"); } } public File snapshotFile( long version, String suffix){ checkValidSnapshotSuffix(suffix); return file(version,suffix); } public File journalFile( long transaction, String suffix){ checkValidJournalSuffix(suffix); return file(transaction,suffix); } private File file( long version, String suffix){ String fileName="0000000000000000000" + version; return new File(_directory,fileName.substring(fileName.length() - DIGITS_IN_FILENAME) + "." + suffix); } /** * Returns -1 if fileName is not the name of a snapshot file. */ public static long snapshotVersion( File file){ return version(file,SNAPSHOT_FILENAME_PATTERN); } /** * Returns -1 if fileName is not the name of a journal file. */ public static long journalVersion( File file){ return version(file,JOURNAL_FILENAME_PATTERN); } private static long version( File file, String filenamePattern){ String fileName=file.getName(); if (!fileName.matches(filenamePattern)) return -1; return Long.parseLong(fileName.substring(0,fileName.indexOf("."))); } /** * Find the latest snapshot file. Returns null if no snapshot file was found. */ public File latestSnapshot() throws IOException { File[] files=_directory.listFiles(); if (files == null) throw new IOException("Error reading file list from directory " + _directory); File latestSnapshot=null; long latestVersion=0; for (int i=0; i < files.length; i++) { File candidateSnapshot=files[i]; long candidateVersion=snapshotVersion(candidateSnapshot); if (candidateVersion > latestVersion) { latestVersion=candidateVersion; latestSnapshot=candidateSnapshot; } } return latestSnapshot; } public File findInitialJournalFile( long initialTransactionWanted){ File[] journals=_directory.listFiles(new FileFilter(){ public boolean accept( File pathname){ return pathname.getName().matches(JOURNAL_FILENAME_PATTERN); } } ); Arrays.sort(journals,new Comparator(){ public int compare( Object o1, Object o2){ File f1=(File)o1; File f2=(File)o2; return new Long(journalVersion(f1)).compareTo(new Long(journalVersion(f2))); } } ); for (int i=journals.length - 1; i >= 0; i--) { File journal=journals[i]; long version=journalVersion(journal); if (version <= initialTransactionWanted) { return journal; } } return null; } public File createTempFile( String prefix, String suffix) throws IOException { return File.createTempFile(prefix,suffix,_directory); } public static void renameUnusedFile( File journalFile){ journalFile.renameTo(new File(journalFile.getAbsolutePath() + ".unusedFile" + System.currentTimeMillis())); } }