diff --git a/components/net/sf/briar/db/DatabaseCleaner.java b/components/net/sf/briar/db/DatabaseCleaner.java index 32ecfe316d1814bed034232cc3442bae2ba735c7..639c92d4e34a47f979e4cf4b3843ec666df49798 100644 --- a/components/net/sf/briar/db/DatabaseCleaner.java +++ b/components/net/sf/briar/db/DatabaseCleaner.java @@ -19,9 +19,9 @@ interface DatabaseCleaner { /** * Checks how much free storage space is available to the database, and * if necessary expires old messages until the free space is at least - * DatabaseConstants.MIN_FREE_SPACE. While the free space is less than - * DatabaseConstants.CRITICAL_FREE_SPACE, operations that attempt to - * store messages in the database will block. + * DatabaseConstants.MIN_FREE_SPACE. If the free space is less than + * DatabaseConstants.CRITICAL_FREE_SPACE and there are no more messages + * to expire, an Error will be thrown. */ void checkFreeSpaceAndClean() throws DbException; diff --git a/components/net/sf/briar/db/DatabaseCleanerImpl.java b/components/net/sf/briar/db/DatabaseCleanerImpl.java index 71574267edf86ac0820520838905a6dd02deb365..71322acf822aaaf623eb6b68aee6aed7164e78ea 100644 --- a/components/net/sf/briar/db/DatabaseCleanerImpl.java +++ b/components/net/sf/briar/db/DatabaseCleanerImpl.java @@ -33,12 +33,10 @@ class DatabaseCleanerImpl extends TimerTask implements DatabaseCleaner { callback.checkFreeSpaceAndClean(); } } catch(DbException e) { - if(LOG.isLoggable(Level.WARNING)) - LOG.warning(e.toString()); + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); throw new Error(e); // Kill the application } catch(RuntimeException e) { - if(LOG.isLoggable(Level.WARNING)) - LOG.warning(e.toString()); + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); throw new Error(e); // Kill the application } } diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 408d415095733180af9fd969556a564cae429e8e..ebe17a3097950d204503744ee12830df68567ebc 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -1548,8 +1548,7 @@ DatabaseCleaner.Callback { if(freeSpace < CRITICAL_FREE_SPACE && !expired) { // FIXME: Work out what to do here - the amount of free space // is critically low and there are no messages left to expire - System.err.println("Disk space is critical - shutting down"); - System.exit(1); + throw new Error("Disk space is critical"); } Thread.yield(); freeSpace = db.getFreeSpace(); diff --git a/components/net/sf/briar/db/DatabaseConstants.java b/components/net/sf/briar/db/DatabaseConstants.java index fa013a562d114d3d4657d57bf9150c5d968b1e5a..d177b8c0893197a6ef73d4d0d6cd6827943e46da 100644 --- a/components/net/sf/briar/db/DatabaseConstants.java +++ b/components/net/sf/briar/db/DatabaseConstants.java @@ -14,7 +14,7 @@ interface DatabaseConstants { /** * The minimum amount of space in bytes that must be kept free on the device * where the database is stored. If less than this much space is free and - * there are no more messages to expire, the program will shut down. + * there are no more messages to expire, an Error will be thrown. */ static final long CRITICAL_FREE_SPACE = 100 * 1024 * 1024; // 100 MiB diff --git a/components/net/sf/briar/db/KeyRotator.java b/components/net/sf/briar/db/KeyRotator.java new file mode 100644 index 0000000000000000000000000000000000000000..200a1ed24f1f3f9b1f074aa3ebda8954558b0796 --- /dev/null +++ b/components/net/sf/briar/db/KeyRotator.java @@ -0,0 +1,24 @@ +package net.sf.briar.db; + +import net.sf.briar.api.db.DbException; + +interface KeyRotator { + + /** + * Starts a new thread to rotate keys periodically. The rotator will pause + * for the given number of milliseconds between rotations. + */ + void startRotating(Callback callback, long msBetweenRotations); + + /** Tells the rotator thread to exit. */ + void stopRotating(); + + interface Callback { + + /** + * Rotates keys, replacing and destroying any keys that have passed the + * ends of their respective retention periods. + */ + void rotateKeys() throws DbException; + } +} diff --git a/components/net/sf/briar/db/KeyRotatorImpl.java b/components/net/sf/briar/db/KeyRotatorImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..26db0228b3b5392ba856b6b99135a16e550344f9 --- /dev/null +++ b/components/net/sf/briar/db/KeyRotatorImpl.java @@ -0,0 +1,41 @@ +package net.sf.briar.db; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.sf.briar.api.db.DbException; + +class KeyRotatorImpl extends TimerTask implements KeyRotator { + + private static final Logger LOG = + Logger.getLogger(KeyRotatorImpl.class.getName()); + + private volatile Callback callback = null; + private volatile Timer timer = null; + + public void startRotating(Callback callback, long msBetweenRotations) { + this.callback = callback; + timer = new Timer(); + timer.scheduleAtFixedRate(this, 0L, msBetweenRotations); + } + + public void stopRotating() { + if(timer == null) throw new IllegalStateException(); + timer.cancel(); + } + + public void run() { + if(callback == null) throw new IllegalStateException(); + try { + callback.rotateKeys(); + } catch(DbException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + throw new Error(e); // Kill the application + } catch(RuntimeException e) { + if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.toString()); + throw new Error(e); // Kill the application + } + } +} diff --git a/test/net/sf/briar/db/KeyRotatorImplTest.java b/test/net/sf/briar/db/KeyRotatorImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2470e534e6c1fb7da634552a375888451a96e12e --- /dev/null +++ b/test/net/sf/briar/db/KeyRotatorImplTest.java @@ -0,0 +1,53 @@ +package net.sf.briar.db; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.db.DbException; +import net.sf.briar.db.KeyRotator.Callback; + +import org.junit.Test; + +public class KeyRotatorImplTest extends BriarTestCase { + + @Test + public void testCleanerRunsPeriodically() throws Exception { + final CountDownLatch latch = new CountDownLatch(5); + Callback callback = new Callback() { + + public void rotateKeys() throws DbException { + latch.countDown(); + } + }; + KeyRotatorImpl cleaner = new KeyRotatorImpl(); + // Start the rotator + cleaner.startRotating(callback, 10L); + // The keys should be rotated five times (allow 5 secs for system load) + assertTrue(latch.await(5, TimeUnit.SECONDS)); + // Stop the rotator + cleaner.stopRotating(); + } + + @Test + public void testStoppingCleanerWakesItUp() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + Callback callback = new Callback() { + + public void rotateKeys() throws DbException { + latch.countDown(); + } + }; + KeyRotatorImpl cleaner = new KeyRotatorImpl(); + long start = System.currentTimeMillis(); + // Start the rotator + cleaner.startRotating(callback, 10L * 1000L); + // The keys should be rotated once at startup + assertTrue(latch.await(5, TimeUnit.SECONDS)); + // Stop the rotator (it should be waiting between rotations) + cleaner.stopRotating(); + long end = System.currentTimeMillis(); + // Check that much less than 10 seconds expired + assertTrue(end - start < 10L * 1000L); + } +}