Commit eb360475 authored by akwizgran's avatar akwizgran

KeyRotatorImpl looks a *lot* like DatabaseCleanerImpl.

parent f8183a4c
......@@ -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;
......
......@@ -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
}
}
......
......@@ -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();
......
......@@ -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
......
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;
}
}
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
}
}
}
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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment