From: adam Date: Wed, 16 Mar 2005 10:06:18 +0000 (+0000) Subject: removed prevaylerized caching; major reduction in memory usage X-Git-Url: http://git.megacz.com/?p=org.ibex.mail.git;a=commitdiff_plain;h=3123efa879a3d067e9aa905f51e632f5102c2eb9 removed prevaylerized caching; major reduction in memory usage darcs-hash:20050316100618-5007d-d044b290048e314b871fe86fadc7c5a791a1aa6c.gz --- diff --git a/src/org/ibex/mail/target/FileBasedMailbox.java b/src/org/ibex/mail/target/FileBasedMailbox.java index d11d8ab..428319f 100644 --- a/src/org/ibex/mail/target/FileBasedMailbox.java +++ b/src/org/ibex/mail/target/FileBasedMailbox.java @@ -18,7 +18,6 @@ import java.text.*; public class FileBasedMailbox extends Mailbox.Default { public static final long MAGIC_DATE = 0; - private static final char slash = File.separatorChar; private static final WeakHashMap instances = new WeakHashMap(); public String toString() { return "[FileBasedMailbox " + path.getAbsolutePath() + "]"; } @@ -39,125 +38,12 @@ public class FileBasedMailbox extends Mailbox.Default { } } - // Instance ////////////////////////////////////////////////////////////////////////////// private File path; private FileLock lock; - private Prevayler prevayler; - private Cache cache; - - public static class Cache implements Serializable { - public final int uidValidity = Math.abs(new Random().nextInt()); - private final Hashtable byname = new Hashtable(); - private final Hashtable byuid = new Hashtable(); - private final ArrayList linear = new ArrayList(); - public final File dir; - private int uidNext = 0; - - public Cache(File dir) { this.dir = dir; } - - public void init(final Prevayler prevayler) throws IOException { - dir.mkdirs(); - long time = System.currentTimeMillis(); - Log.info(this, "initializing maildir " + dir.getParent()); - - // Drop entries whose files have vanished - ArrayList kill = new ArrayList(); - for(String s : byname.keySet()) - if (!new File(dir.getParent() + slash + s).exists()) - kill.add(byname.get(s).delete(null)); - for(Transaction t : kill) prevayler.execute(t); - - // Make entries for new files which have appeared - for(String file : new File(dir.getParent()).list()) { - File f = new File(dir.getParent() + slash + file); - if (file.charAt(0)!='.' && !f.isDirectory()) { - Entry e = get(file); - if (e == null) - prevayler.execute(Entry.create(f)); - else if ((f.lastModified() == MAGIC_DATE) != e.seen()) - prevayler.execute(e.seen(f, !e.seen())); - } - } - - // Take a snapshot for posterity - if (System.currentTimeMillis() - time > 1000 * 5) - Log.info(this, " done initializing maildir " + dir.getParent()); - new Thread() { public void run() { - try { prevayler.takeSnapshot(); } catch (Exception e) { Log.error(this, e); } } }.start(); - } - - - public synchronized int size() { return linear.size(); } - public synchronized int uidNext() { return uidNext; } - public synchronized Entry get(int uid) { return byuid.get(uid); } - public synchronized Entry getLinear(int num) { return linear.get(num); } - public synchronized Entry get(String name) { return byname.get(name); } - - public static class Entry implements Serializable { - private final byte[] header; - public final String name; - public final int uid; - private boolean seen; - private Entry(String name, boolean seen, int uid, byte[] header) { - this.name = name; this.seen = seen; this.uid = uid; this.header = header; } - - // Accessors ////////////////////////////////////////////////////////////////////////////// - - public boolean seen() { return seen; } - public Headers headers() { return new Headers(new Stream(new ByteArrayInputStream(header)), true); } - public Message message(Cache cache) { try { - FileInputStream fis = null; - try { - return Message.newMessage(new Fountain.File(new File(cache.dir.getParent() + slash + name))); - } finally { if (fis != null) fis.close(); } - } catch (IOException e) { throw new MailException.IOException(e); - } catch (Message.Malformed e) { throw new MailException(e.getMessage()); } - } - - // Transactions ////////////////////////////////////////////////////////////////////////////// - - public static Transaction create(File f) throws IOException { - final boolean seen = f.lastModified() == MAGIC_DATE; - final String name = f.getName(); - final byte[] header = new Headers(new Stream(new FileInputStream(f)), true).getString().getBytes(); - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - Entry e = new Entry(name, seen, cache.uidNext++, header); - cache.linear.add(e); - cache.byuid.put(e.uid, e); - cache.byname.put(e.name, e); - } } }; - } - - public Transaction seen(File f, final boolean seen) { - f.setLastModified(seen ? MAGIC_DATE : System.currentTimeMillis()); - final int uid = this.uid; - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - cache.get(uid).seen = seen; - } } }; - } - public Transaction delete(File f) { - if (f != null && f.exists()) f.delete(); - final int uid = this.uid; - return new Transaction() { - public void executeOn(Object o, Date now) { - Cache cache = (Cache)o; - synchronized(cache) { - Cache.Entry e = cache.get(uid); - cache.byname.remove(e.name); - cache.linear.remove(e); - cache.byuid.remove(uid); - } } }; - } - } - } + private int uidNext; + private int uidValidity; // Helpers ////////////////////////////////////////////////////////////////////////////// @@ -173,27 +59,23 @@ public class FileBasedMailbox extends Mailbox.Default { path.mkdirs(); // acquire lock - lock = new RandomAccessFile(this.path.getAbsolutePath() + slash + ".lock", "rw").getChannel().tryLock(); + File lockfile = new File(this.path.getAbsolutePath() + slash + ".lock"); + lock = new RandomAccessFile(lockfile, "rw").getChannel().tryLock(); if (lock == null) throw new IOException("unable to lock FileBasedMailbox"); - - File cacheDir = new File(path.getAbsolutePath() + slash + ".cache"); - try { - prevayler = PrevaylerFactory.createPrevayler(new Cache(cacheDir), cacheDir.getAbsolutePath()); - cache = (Cache)prevayler.prevalentSystem(); - } catch (Throwable t) { - Log.warn(this, "error while attempting to reconstitute a FileBasedMailbox.cache:"); - Log.warn(this, t); - Log.warn(this, "discarding cache..."); - rmDashRf(cacheDir); - prevayler = PrevaylerFactory.createPrevayler(new Cache(cacheDir), cacheDir.getAbsolutePath()); - cache = (Cache)prevayler.prevalentSystem(); + uidValidity = (int)(lockfile.lastModified() & 0xffffffff); + uidNext = 0; + String[] files = path.list(); + for(int i=0; i=uidNext) uidNext = n; + } catch(Exception e) { /* DELIBERATE */ } } - cache.init(prevayler); } public Mailbox.Iterator iterator() { return new Iterator(); } - public int uidValidity() { return cache.uidValidity; } public synchronized void add(Message message) { add(message, Mailbox.Flag.RECENT); } public String[] children() { Vec vec = new Vec(); @@ -205,11 +87,12 @@ public class FileBasedMailbox extends Mailbox.Default { return (String[])vec.copyInto(new String[vec.size()]); } - public int uidNext() { return cache.uidNext(); } + public int uidValidity() { return uidValidity; } + public int uidNext() { return uidNext; } public synchronized void add(Message message, int flags) { try { String name, fullname; File target, f; - for(int i = cache.uidNext(); ; i++) { + for(int i = uidNext; ; i++) { name = i + "."; fullname = path.getAbsolutePath() + slash + name; target = new File(fullname); @@ -221,27 +104,42 @@ public class FileBasedMailbox extends Mailbox.Default { message.getStream().transcribe(stream); stream.close(); f.renameTo(new File(fullname)); + uidNext++; f = new File(fullname); if ((flags & Mailbox.Flag.SEEN) == Mailbox.Flag.SEEN) f.setLastModified(MAGIC_DATE); - prevayler.execute(Cache.Entry.create(f)); } catch (IOException e) { throw new MailException.IOException(e); } Log.info(this, path + " <= " + message.summary()); } private class Iterator extends Mailbox.Default.Iterator { int cur = -1; - private Cache.Entry entry() { return cache.getLinear(cur); } - private File file() { return new File(cache.dir.getParent() + slash + entry().name); } - public Headers head() { return done() ? null : entry().headers(); } - public boolean done() { return cur >= cache.size(); } + String[] files = path.list(new FilenameFilter() { public boolean accept(File dir, String name) { + return name.endsWith("."); + } }); + private File file() { return new File(path.getAbsolutePath() + slash + files[cur]); } + public boolean done() { return cur >= files.length; } public boolean next() { cur++; return !done(); } - public boolean seen() { return done() ? false : entry().seen(); } + public boolean seen() { return false; } public boolean recent() { return false; } public int num() { return cur+1; } // EUDORA insists that message numbers start at 1, not 0 - public int uid() { return done() ? -1 : entry().uid; } - public void delete() { prevayler.execute(entry().delete(file())); } - public void seen(boolean seen) { prevayler.execute(entry().seen(file(), seen)); } - public Message cur() { return entry().message(cache); } + public int uid() { return done() ? -1 : Integer.parseInt(files[cur].substring(0, files[cur].length()-1)); } + public void delete() { File f = file(); if (f != null && f.exists()) f.delete(); } + public void seen(boolean seen) { } + public Headers head() { + if (done()) return null; + FileInputStream fis = null; + try { + return new Headers(new Stream(new FileInputStream(file()))); + } catch (IOException e) { throw new MailException.IOException(e); + } finally { if (fis != null) try { fis.close(); } catch (Exception e) { /* DELIBERATE */ } } + } + public Message cur() { + FileInputStream fis = null; + try { + return Message.newMessage(new Fountain.File(file())); + //} catch (IOException e) { throw new MailException.IOException(e); + } catch (Message.Malformed e) { throw new MailException(e.getMessage()); + } finally { if (fis != null) try { fis.close(); } catch (Exception e) { /* DELIBERATE */ } } + } } - }