Skip to content
Snippets Groups Projects
Commit f6df3337 authored by akwizgran's avatar akwizgran
Browse files

Changed drive monitors to use callbacks rather than blocking.

parent 3a321b0f
No related branches found
No related tags found
No related merge requests found
Showing
with 253 additions and 148 deletions
package net.sf.briar.api.transport.batch;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.transport.TransportCallback;
/**
......@@ -8,7 +10,9 @@ import net.sf.briar.api.transport.TransportCallback;
*/
public interface BatchTransportCallback extends TransportCallback {
void readerCreated(BatchTransportReader r);
void readerCreated(ContactId contactId, byte[] encryptedIv,
BatchTransportReader r);
void writerCreated(BatchTransportWriter w);
void writerCreated(ContactId contactId, TransportId t, long connection,
BatchTransportWriter w);
}
package net.sf.briar.api.transport.batch;
import java.io.IOException;
import java.util.Map;
import net.sf.briar.api.ContactId;
......@@ -23,13 +24,13 @@ public interface BatchTransportPlugin {
void start(Map<String, String> localProperties,
Map<ContactId, Map<String, String>> remoteProperties,
Map<String, String> config, BatchTransportCallback c)
throws InvalidTransportException, InvalidConfigException;
throws InvalidTransportException, InvalidConfigException, IOException;
/**
* Stops the plugin. No further connections will be passed to the callback
* after this method has returned.
*/
void stop();
void stop() throws IOException;
/** Updates the plugin's local transport properties. */
void setLocalProperties(Map<String, String> properties)
......
package net.sf.briar.api.transport.stream;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.transport.TransportCallback;
/**
......@@ -8,5 +10,9 @@ import net.sf.briar.api.transport.TransportCallback;
*/
public interface StreamTransportCallback extends TransportCallback {
void connectionCreated(StreamTransportConnection c);
void incomingConnectionCreated(ContactId contactId, byte[] encryptedIv,
StreamTransportConnection c);
void outgoingConnectionCreated(ContactId contactId, TransportId t,
long connection, StreamTransportConnection c);
}
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.transport.ConnectionRecogniser;
import net.sf.briar.api.transport.InvalidConfigException;
import net.sf.briar.api.transport.InvalidTransportException;
import net.sf.briar.api.transport.TransportConstants;
......@@ -19,6 +22,8 @@ import org.apache.commons.io.FileSystemUtils;
abstract class FilePlugin implements BatchTransportPlugin {
private final ConnectionRecogniser recogniser;
protected Map<String, String> localProperties = null;
protected Map<ContactId, Map<String, String>> remoteProperties = null;
protected Map<String, String> config = null;
......@@ -28,10 +33,14 @@ abstract class FilePlugin implements BatchTransportPlugin {
protected abstract File chooseOutputDirectory();
protected abstract void writerFinished(File f);
FilePlugin(ConnectionRecogniser recogniser) {
this.recogniser = recogniser;
}
public synchronized void start(Map<String, String> localProperties,
Map<ContactId, Map<String, String>> remoteProperties,
Map<String, String> config, BatchTransportCallback callback)
throws InvalidTransportException, InvalidConfigException {
throws InvalidTransportException, InvalidConfigException, IOException {
if(started) throw new IllegalStateException();
started = true;
this.localProperties = localProperties;
......@@ -40,7 +49,7 @@ abstract class FilePlugin implements BatchTransportPlugin {
this.callback = callback;
}
public synchronized void stop() {
public synchronized void stop() throws IOException {
if(!started) throw new IllegalStateException();
started = false;
}
......@@ -106,4 +115,37 @@ abstract class FilePlugin implements BatchTransportPlugin {
protected long getCapacity(String path) throws IOException {
return FileSystemUtils.freeSpaceKb(path) * 1024L;
}
protected void createReaderFromFile(File f) {
if(!isPossibleConnectionFilename(f.getName())) return;
if(f.length() < TransportConstants.MIN_CONNECTION_LENGTH) return;
try {
FileInputStream in = new FileInputStream(f);
byte[] iv = new byte[TransportConstants.IV_LENGTH];
int offset = 0;
while(offset < iv.length) {
int read = in.read(iv, offset, iv.length - offset);
if(read == -1) break;
offset += read;
}
ContactId c = recogniser.acceptConnection(iv);
if(c == null) {
// Nobody there
in.close();
return;
}
FileTransportReader reader = new FileTransportReader(f, in);
callback.readerCreated(c, iv, reader);
} catch(DbException e) {
// FIXME: At least log it
return;
} catch(IOException e) {
// FIXME: At least log it
return;
}
}
protected boolean isPossibleConnectionFilename(String filename) {
return filename.toLowerCase().matches("[a-z]{8}\\.dat");
}
}
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import net.sf.briar.api.transport.batch.BatchTransportReader;
class FileTransportReader implements BatchTransportReader {
private final File file;
private final InputStream in;
private boolean streamInUse = false;
FileTransportReader(File file, InputStream in) {
this.file = file;
this.in = in;
}
public InputStream getInputStream() {
streamInUse = true;
return in;
}
public void dispose() throws IOException {
if(streamInUse) in.close();
file.delete();
}
}
......@@ -2,52 +2,39 @@ package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
private final RemovableDriveFinder finder;
private final long pollingInterval;
private final LinkedList<File> inserted;
private final LinkedList<IOException> exceptions;
private final Object pollingLock;
private final Object pollingLock = new Object();
private boolean started = false, stopped = false;
private Thread pollingThread = null;
private volatile boolean running = false;
private volatile Callback callback = null;
private volatile IOException exception = null;
public PollingRemovableDriveMonitor(RemovableDriveFinder finder,
long pollingInterval) {
this.finder = finder;
this.pollingInterval = pollingInterval;
inserted = new LinkedList<File>();
exceptions = new LinkedList<IOException>();
pollingLock = new Object();
}
public synchronized void start() throws IOException {
if(started || stopped) throw new IllegalStateException();
started = true;
pollingThread = new Thread(this);
pollingThread.start();
}
public synchronized File waitForInsertion() throws IOException {
if(!started || stopped) throw new IllegalStateException();
if(!exceptions.isEmpty()) throw exceptions.poll();
while(inserted.isEmpty()) {
try {
wait();
} catch(InterruptedException ignored) {}
if(!exceptions.isEmpty()) throw exceptions.poll();
}
return inserted.poll();
public synchronized void start(Callback callback) throws IOException {
if(running) throw new IllegalStateException();
running = true;
this.callback = callback;
new Thread(this).start();
}
public synchronized void stop() throws IOException {
if(!started || stopped) throw new IllegalStateException();
if(!exceptions.isEmpty()) throw exceptions.poll();
stopped = true;
if(!running) throw new IllegalStateException();
running = false;
if(exception != null) {
IOException e = exception;
exception = null;
throw e;
}
synchronized(pollingLock) {
pollingLock.notifyAll();
}
......@@ -56,34 +43,21 @@ class PollingRemovableDriveMonitor implements RemovableDriveMonitor, Runnable {
public void run() {
try {
List<File> drives = finder.findRemovableDrives();
while(true) {
synchronized(this) {
if(stopped) return;
}
while(running) {
synchronized(pollingLock) {
try {
pollingLock.wait(pollingInterval);
} catch(InterruptedException ignored) {}
}
synchronized(this) {
if(stopped) return;
}
if(!running) return;
List<File> newDrives = finder.findRemovableDrives();
for(File f : newDrives) {
if(!drives.contains(f)) {
synchronized(this) {
inserted.add(f);
notifyAll();
}
}
if(!drives.contains(f)) callback.driveInserted(f);
}
drives = newDrives;
}
} catch(IOException e) {
synchronized(this) {
exceptions.add(e);
notifyAll();
}
exception = e;
}
}
}
......@@ -5,9 +5,12 @@ import java.io.IOException;
interface RemovableDriveMonitor {
void start() throws IOException;
File waitForInsertion() throws IOException;
void start(Callback c) throws IOException;
void stop() throws IOException;
interface Callback {
void driveInserted(File f);
}
}
......@@ -3,25 +3,51 @@ package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.transport.ConnectionRecogniser;
import net.sf.briar.api.transport.InvalidConfigException;
import net.sf.briar.api.transport.InvalidTransportException;
import net.sf.briar.api.transport.batch.BatchTransportCallback;
class RemovableDrivePlugin extends FilePlugin {
class RemovableDrivePlugin extends FilePlugin
implements RemovableDriveMonitor.Callback {
public static final int TRANSPORT_ID = 0;
private static final TransportId id = new TransportId(TRANSPORT_ID);
private final RemovableDriveFinder finder;
private final RemovableDriveMonitor monitor;
RemovableDrivePlugin(RemovableDriveFinder finder) {
RemovableDrivePlugin(ConnectionRecogniser recogniser,
RemovableDriveFinder finder, RemovableDriveMonitor monitor) {
super(recogniser);
this.finder = finder;
this.monitor = monitor;
}
public TransportId getId() {
return id;
}
@Override
public void start(Map<String, String> localProperties,
Map<ContactId, Map<String, String>> remoteProperties,
Map<String, String> config, BatchTransportCallback callback)
throws InvalidTransportException, InvalidConfigException, IOException {
super.start(localProperties, remoteProperties, config, callback);
monitor.start(this);
}
@Override
public void stop() throws IOException {
super.stop();
monitor.stop();
}
@Override
protected File chooseOutputDirectory() {
try {
......@@ -43,4 +69,9 @@ class RemovableDrivePlugin extends FilePlugin {
protected void writerFinished(File f) {
callback.showMessage("REMOVABLE_DRIVE_WRITE_FINISHED");
}
public void driveInserted(File root) {
File[] files = root.listFiles();
if(files != null) for(File f : files) createReaderFromFile(f);
}
}
......@@ -3,7 +3,6 @@ package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import net.contentobjects.jnotify.JNotify;
......@@ -13,15 +12,16 @@ abstract class UnixRemovableDriveMonitor implements RemovableDriveMonitor,
JNotifyListener {
private final List<Integer> watches = new ArrayList<Integer>();
private final LinkedList<File> inserted = new LinkedList<File>();
private boolean started = false, stopped = false;
private boolean started = false;
private Callback callback = null;
protected abstract String[] getPathsToWatch();
public synchronized void start() throws IOException {
if(started || stopped) throw new IllegalStateException();
public synchronized void start(Callback callback) throws IOException {
if(started) throw new IllegalStateException();
started = true;
this.callback = callback;
int mask = JNotify.FILE_CREATED;
for(String path : getPathsToWatch()) {
if(new File(path).exists())
......@@ -29,26 +29,18 @@ JNotifyListener {
}
}
public synchronized File waitForInsertion() throws IOException {
if(!started || stopped) throw new IllegalStateException();
while(inserted.isEmpty()) {
try {
wait();
} catch(InterruptedException ignored) {}
}
return inserted.poll();
}
public synchronized void stop() throws IOException {
if(!started || stopped) throw new IllegalStateException();
stopped = true;
if(!started) throw new IllegalStateException();
started = false;
callback = null;
for(Integer w : watches) JNotify.removeWatch(w);
watches.clear();
}
public void fileCreated(int wd, String rootPath, String name) {
synchronized(this) {
inserted.add(new File(rootPath + "/" + name));
notifyAll();
if(!started) throw new IllegalStateException();
callback.driveInserted(new File(rootPath + "/" + name));
}
}
......
......@@ -10,6 +10,8 @@ import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
......@@ -18,15 +20,13 @@ public class PollingRemovableDriveMonitorTest extends TestCase {
@Test
public void testOneCallbackPerFile() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final List<File> detected = new ArrayList<File>();
final File file1 = new File("foo");
final File file2 = new File("bar");
// Create a finder that returns no files the first time, then two files
final List<File> noDrives = Collections.emptyList();
final List<File> twoDrives = new ArrayList<File>();
twoDrives.add(file1);
twoDrives.add(file2);
// Create a finder that returns no files the first time, then two files
Mockery context = new Mockery();
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
......@@ -36,24 +36,21 @@ public class PollingRemovableDriveMonitorTest extends TestCase {
oneOf(finder).findRemovableDrives();
will(returnValue(twoDrives));
}});
// Create a monitor that will wait for two files before stopping
// Create a callback that will wait for two files before stopping
final List<File> detected = new ArrayList<File>();
final CountDownLatch latch = new CountDownLatch(2);
final Callback callback = new Callback() {
public void driveInserted(File f) {
detected.add(f);
latch.countDown();
}
};
// Create the monitor and start it
final RemovableDriveMonitor monitor =
new PollingRemovableDriveMonitor(finder, 10);
monitor.start();
new Thread() {
@Override
public void run() {
try {
detected.add(monitor.waitForInsertion());
detected.add(monitor.waitForInsertion());
latch.countDown();
} catch(IOException e) {
fail();
}
}
}.start();
monitor.start(callback);
// Wait for the monitor to detect the files
assertTrue(latch.await(2, TimeUnit.SECONDS));
assertTrue(latch.await(1, TimeUnit.SECONDS));
monitor.stop();
// Check that both files were detected
assertEquals(2, detected.size());
......@@ -63,32 +60,6 @@ public class PollingRemovableDriveMonitorTest extends TestCase {
context.assertIsSatisfied();
}
@Test
public void testExceptionRethrownWhenWaiting() throws Exception {
final List<File> noDrives = Collections.emptyList();
// Create a finder that throws an exception the second time it's polled
Mockery context = new Mockery();
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
context.checking(new Expectations() {{
oneOf(finder).findRemovableDrives();
will(returnValue(noDrives));
oneOf(finder).findRemovableDrives();
will(throwException(new IOException()));
}});
// The monitor should rethrow the exception when it waits
final RemovableDriveMonitor monitor =
new PollingRemovableDriveMonitor(finder, 10);
monitor.start();
try {
monitor.waitForInsertion();
fail();
} catch(IOException expected) {}
// The exception shouldn't be thrown again
monitor.stop();
context.assertIsSatisfied();
}
@Test
public void testExceptionRethrownWhenStopping() throws Exception {
final List<File> noDrives = Collections.emptyList();
......@@ -102,11 +73,12 @@ public class PollingRemovableDriveMonitorTest extends TestCase {
oneOf(finder).findRemovableDrives();
will(throwException(new IOException()));
}});
// The monitor should rethrow the exception when it stops
// Create the monitor, start it, and give it some time to run
final RemovableDriveMonitor monitor =
new PollingRemovableDriveMonitor(finder, 10);
monitor.start();
monitor.start(null);
Thread.sleep(50);
// The monitor should rethrow the exception when it stops
try {
monitor.stop();
fail();
......
......@@ -9,8 +9,10 @@ import java.util.List;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.transport.ConnectionRecogniser;
import net.sf.briar.api.transport.batch.BatchTransportCallback;
import net.sf.briar.api.transport.batch.BatchTransportWriter;
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
import org.jmock.Expectations;
import org.jmock.Mockery;
......@@ -31,9 +33,14 @@ public class RemovableDrivePluginTest extends TestCase {
@Test
public void testGetId() {
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
assertEquals(RemovableDrivePlugin.TRANSPORT_ID,
plugin.getId().getInt());
......@@ -46,15 +53,23 @@ public class RemovableDrivePluginTest extends TestCase {
final List<File> drives = Collections.emptyList();
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
assertNull(plugin.createWriter(contactId));
......@@ -71,18 +86,26 @@ public class RemovableDrivePluginTest extends TestCase {
drives.add(drive2);
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String.class)),
with(any(String[].class)));
will(returnValue(-1)); // The user cancelled the choice
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
assertNull(plugin.createWriter(contactId));
......@@ -101,18 +124,26 @@ public class RemovableDrivePluginTest extends TestCase {
drives.add(drive2);
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String.class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1 but it doesn't exist
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
assertNull(plugin.createWriter(contactId));
......@@ -133,18 +164,26 @@ public class RemovableDrivePluginTest extends TestCase {
assertTrue(drive1.createNewFile());
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String.class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1 but it's not a dir
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
assertNull(plugin.createWriter(contactId));
......@@ -165,18 +204,26 @@ public class RemovableDrivePluginTest extends TestCase {
assertTrue(drive1.mkdir());
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String.class)),
with(any(String[].class)));
will(returnValue(0)); // The user chose drive1
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
assertNotNull(plugin.createWriter(contactId));
......@@ -200,11 +247,17 @@ public class RemovableDrivePluginTest extends TestCase {
assertTrue(drive1.mkdir());
Mockery context = new Mockery();
final ConnectionRecogniser recogniser =
context.mock(ConnectionRecogniser.class);
final RemovableDriveFinder finder =
context.mock(RemovableDriveFinder.class);
final RemovableDriveMonitor monitor =
context.mock(RemovableDriveMonitor.class);
final BatchTransportCallback callback =
context.mock(BatchTransportCallback.class);
context.checking(new Expectations() {{
oneOf(monitor).start(with(any(Callback.class)));
oneOf(finder).findRemovableDrives();
will(returnValue(drives));
oneOf(callback).showChoice(with(any(String.class)),
......@@ -212,7 +265,9 @@ public class RemovableDrivePluginTest extends TestCase {
will(returnValue(0)); // The user chose drive1
oneOf(callback).showMessage(with(any(String.class)));
}});
RemovableDrivePlugin plugin = new RemovableDrivePlugin(finder);
RemovableDrivePlugin plugin = new RemovableDrivePlugin(recogniser,
finder, monitor);
plugin.start(null, null, null, callback);
BatchTransportWriter writer = plugin.createWriter(contactId);
......
package net.sf.briar.plugins.file;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
......@@ -9,6 +8,7 @@ import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import net.sf.briar.TestUtils;
import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback;
import org.junit.After;
import org.junit.Before;
......@@ -27,36 +27,31 @@ public class UnixRemovableDriveMonitorTest extends TestCase {
public void testNonexistentDir() throws Exception {
File doesNotExist = new File(testDir, "doesNotExist");
RemovableDriveMonitor monitor = createMonitor(doesNotExist);
monitor.start();
monitor.start(null);
monitor.stop();
}
@Test
public void testOneCallbackPerFile() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
// Create a callback that will wait for two files before stopping
final List<File> detected = new ArrayList<File>();
// Create a monitor that will wait for two files before stopping
final RemovableDriveMonitor monitor = createMonitor(testDir);
monitor.start();
new Thread() {
@Override
public void run() {
try {
detected.add(monitor.waitForInsertion());
detected.add(monitor.waitForInsertion());
latch.countDown();
} catch(IOException e) {
fail();
}
final CountDownLatch latch = new CountDownLatch(2);
final Callback callback = new Callback() {
public void driveInserted(File f) {
detected.add(f);
latch.countDown();
}
}.start();
};
// Create the monitor and start it
RemovableDriveMonitor monitor = createMonitor(testDir);
monitor.start(callback);
// Create two files in the test directory
File file1 = new File(testDir, "1");
File file2 = new File(testDir, "2");
assertTrue(file1.createNewFile());
assertTrue(file2.createNewFile());
// Wait for the monitor to detect the files
assertTrue(latch.await(2, TimeUnit.SECONDS));
assertTrue(latch.await(1, TimeUnit.SECONDS));
monitor.stop();
// Check that both files were detected
assertEquals(2, detected.size());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment