package com.onionnetworks.util; import java.io.*; import java.util.*; /** * @author Justin F. Chapweske */ public class AsyncPersistentProps implements Runnable { private File f; private Properties p; private IOException ioe; private boolean closed; private boolean changed, writing; /** * Reads in the properties from the file if it exists. If the file * does not exist then the file and an empty Properties will be created */ public AsyncPersistentProps(File f) throws IOException { this.f = f; p = new Properties(); if (f.exists()) { p.load(new FileInputStream(f)); } final Thread thread = new Thread(this,"Props Writer :"+f.getName()); thread.setDaemon(true); thread.start(); } public Properties getProperties() { return p; } public File getFile() { return f; } public synchronized Object setProperty(String key, String value) { checkState(); Object result = p.setProperty(key,value); changed = true; this.notifyAll(); return result; } public synchronized Object remove(Object key) { checkState(); Object result = p.remove(key); if (result != null) { changed = true; this.notifyAll(); } return result; } public synchronized void clear() { checkState(); p.clear(); changed = true; this.notifyAll(); } public synchronized String getProperty(String key) { return p.getProperty(key); } public synchronized void flush() throws IOException { while (!closed && (changed || writing)) { try { this.wait(); } catch (InterruptedException e) { throw new InterruptedIOException(e.getMessage()); } } if (ioe != null) { /* this code avoids throw {} finally {} for the sake of GCJ 3.0 */ IOException ex = ioe; ioe = null; throw ex; } } public synchronized void close() throws IOException { flush(); // This will toss the exception if set. closed = true; this.notifyAll(); } private synchronized void fail(IOException e) { closed = true; ioe = e; this.notifyAll(); } private void checkState() { if (ioe != null) { throw new IllegalStateException(ioe.getMessage()); } else if (closed) { throw new IllegalStateException("Sorry, we're closed"); } } public void run() { while (true) { try { byte[] b = null; synchronized (this) { if (closed) { return; } // If its not changed, then wait. if (!changed) { try { this.wait(); } catch (InterruptedException e) { fail(new InterruptedIOException(e.getMessage())); } if (!changed) { continue; } } //snapshot the Properties into a byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); p.store(baos,null); b = baos.toByteArray(); changed = false; writing = true; } //Write the snapshot to disk. if (f.exists()) { f.delete(); } FileOutputStream fos = new FileOutputStream(f); fos.write(b); fos.flush(); fos.close(); // Notify that we're done writing. synchronized (this) { writing = false; this.notifyAll(); } } catch (IOException e) { fail(e); } } } }