mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Converted ReliabilityLayer into an interface for better testability.
This commit is contained in:
@@ -5,12 +5,15 @@ import java.util.concurrent.Executor;
|
||||
class ModemFactoryImpl implements ModemFactory {
|
||||
|
||||
private final Executor executor;
|
||||
private final ReliabilityLayerFactory reliabilityFactory;
|
||||
|
||||
ModemFactoryImpl(Executor executor) {
|
||||
ModemFactoryImpl(Executor executor,
|
||||
ReliabilityLayerFactory reliabilityFactory) {
|
||||
this.executor = executor;
|
||||
this.reliabilityFactory = reliabilityFactory;
|
||||
}
|
||||
|
||||
public Modem createModem(Modem.Callback callback, String portName) {
|
||||
return new ModemImpl(executor, callback, portName);
|
||||
return new ModemImpl(executor, reliabilityFactory, callback, portName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
private static final int CONNECT_TIMEOUT = 2 * 60 * 1000; // Milliseconds
|
||||
|
||||
private final Executor executor;
|
||||
private final ReliabilityLayerFactory reliabilityFactory;
|
||||
private final Callback callback;
|
||||
private final SerialPort port;
|
||||
private final Semaphore stateChange;
|
||||
@@ -36,11 +37,13 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
|
||||
private int lineLen = 0;
|
||||
|
||||
private ReliabilityLayer reliabilityLayer = null; // Locking: this
|
||||
private ReliabilityLayer reliability = null; // Locking: this
|
||||
private boolean initialised = false, connected = false; // Locking: this
|
||||
|
||||
ModemImpl(Executor executor, Callback callback, String portName) {
|
||||
ModemImpl(Executor executor, ReliabilityLayerFactory reliabilityFactory,
|
||||
Callback callback, String portName) {
|
||||
this.executor = executor;
|
||||
this.reliabilityFactory = reliabilityFactory;
|
||||
this.callback = callback;
|
||||
port = new SerialPort(portName);
|
||||
stateChange = new Semaphore(1);
|
||||
@@ -142,18 +145,18 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
|
||||
// Locking: stateChange
|
||||
private void hangUpInner() throws IOException {
|
||||
ReliabilityLayer reliabilityLayer;
|
||||
ReliabilityLayer reliability;
|
||||
synchronized(this) {
|
||||
if(this.reliabilityLayer == null) {
|
||||
if(this.reliability == null) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Not hanging up - already on the hook");
|
||||
return;
|
||||
}
|
||||
reliabilityLayer = this.reliabilityLayer;
|
||||
this.reliabilityLayer = null;
|
||||
reliability = this.reliability;
|
||||
this.reliability = null;
|
||||
connected = false;
|
||||
}
|
||||
reliabilityLayer.stop();
|
||||
reliability.stop();
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Hanging up");
|
||||
try {
|
||||
port.setDTR(false);
|
||||
@@ -170,22 +173,22 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ReliabilityLayer reliabilityLayer =
|
||||
new ReliabilityLayer(executor, this);
|
||||
ReliabilityLayer reliability =
|
||||
reliabilityFactory.createReliabilityLayer(this);
|
||||
synchronized(this) {
|
||||
if(!initialised) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Not dialling - modem not initialised");
|
||||
return false;
|
||||
}
|
||||
if(this.reliabilityLayer != null) {
|
||||
if(this.reliability != null) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Not dialling - call in progress");
|
||||
return false;
|
||||
}
|
||||
this.reliabilityLayer = reliabilityLayer;
|
||||
this.reliability = reliability;
|
||||
}
|
||||
reliabilityLayer.start();
|
||||
reliability.start();
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Dialling");
|
||||
try {
|
||||
String dial = "ATDT" + number + "\r\n";
|
||||
@@ -218,21 +221,21 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
ReliabilityLayer reliabilityLayer;
|
||||
ReliabilityLayer reliability;
|
||||
synchronized(this) {
|
||||
reliabilityLayer = this.reliabilityLayer;
|
||||
reliability = this.reliability;
|
||||
}
|
||||
if(reliabilityLayer == null) throw new IOException("Not connected");
|
||||
return reliabilityLayer.getInputStream();
|
||||
if(reliability == null) throw new IOException("Not connected");
|
||||
return reliability.getInputStream();
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
ReliabilityLayer reliabilityLayer;
|
||||
ReliabilityLayer reliability;
|
||||
synchronized(this) {
|
||||
reliabilityLayer = this.reliabilityLayer;
|
||||
reliability = this.reliability;
|
||||
}
|
||||
if(reliabilityLayer == null) throw new IOException("Not connected");
|
||||
return reliabilityLayer.getOutputStream();
|
||||
if(reliability == null) throw new IOException("Not connected");
|
||||
return reliability.getOutputStream();
|
||||
}
|
||||
|
||||
public void hangUp() throws IOException {
|
||||
@@ -280,12 +283,12 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
}
|
||||
|
||||
private boolean handleData(byte[] b) throws IOException {
|
||||
ReliabilityLayer reliabilityLayer;
|
||||
ReliabilityLayer reliability;
|
||||
synchronized(this) {
|
||||
reliabilityLayer = this.reliabilityLayer;
|
||||
reliability = this.reliability;
|
||||
}
|
||||
if(reliabilityLayer == null) return false;
|
||||
reliabilityLayer.handleRead(b);
|
||||
if(reliability == null) return false;
|
||||
reliability.handleRead(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -349,22 +352,22 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ReliabilityLayer reliabilityLayer =
|
||||
new ReliabilityLayer(executor, this);
|
||||
ReliabilityLayer reliability =
|
||||
reliabilityFactory.createReliabilityLayer(this);
|
||||
synchronized(this) {
|
||||
if(!initialised) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Not answering - modem not initialised");
|
||||
return;
|
||||
}
|
||||
if(this.reliabilityLayer != null) {
|
||||
if(this.reliability != null) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Not answering - call in progress");
|
||||
return;
|
||||
}
|
||||
this.reliabilityLayer = reliabilityLayer;
|
||||
this.reliability = reliability;
|
||||
}
|
||||
reliabilityLayer.start();
|
||||
reliability.start();
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Answering");
|
||||
try {
|
||||
port.writeBytes("ATA\r\n".getBytes("US-ASCII"));
|
||||
|
||||
@@ -28,7 +28,10 @@ public class ModemPluginFactory implements DuplexPluginFactory {
|
||||
// This plugin is not enabled by default
|
||||
String enabled = callback.getConfig().get("enabled");
|
||||
if(StringUtils.isNullOrEmpty(enabled)) return null;
|
||||
ModemFactory modemFactory = new ModemFactoryImpl(pluginExecutor);
|
||||
ReliabilityLayerFactory reliabilityFactory =
|
||||
new ReliabilityLayerFactoryImpl(pluginExecutor);
|
||||
ModemFactory modemFactory = new ModemFactoryImpl(pluginExecutor,
|
||||
reliabilityFactory);
|
||||
return new ModemPlugin(pluginExecutor, modemFactory, callback,
|
||||
POLLING_INTERVAL);
|
||||
}
|
||||
|
||||
@@ -1,103 +1,15 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
class ReliabilityLayer implements ReadHandler, WriteHandler {
|
||||
interface ReliabilityLayer extends ReadHandler, WriteHandler {
|
||||
|
||||
private static final int TICK_INTERVAL = 500; // Milliseconds
|
||||
void start();
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ReliabilityLayer.class.getName());
|
||||
void stop();
|
||||
|
||||
private final Executor executor;
|
||||
private final WriteHandler writeHandler;
|
||||
private final BlockingQueue<byte[]> writes;
|
||||
InputStream getInputStream();
|
||||
|
||||
private volatile Receiver receiver = null;
|
||||
private volatile SlipDecoder decoder = null;
|
||||
private volatile ReceiverInputStream inputStream = null;
|
||||
private volatile SenderOutputStream outputStream = null;
|
||||
private volatile boolean running = false;
|
||||
|
||||
ReliabilityLayer(Executor executor, WriteHandler writeHandler) {
|
||||
this.executor = executor;
|
||||
this.writeHandler = writeHandler;
|
||||
writes = new LinkedBlockingQueue<byte[]>();
|
||||
}
|
||||
|
||||
void start() {
|
||||
SlipEncoder encoder = new SlipEncoder(this);
|
||||
final Sender sender = new Sender(encoder);
|
||||
receiver = new Receiver(sender);
|
||||
decoder = new SlipDecoder(receiver);
|
||||
inputStream = new ReceiverInputStream(receiver);
|
||||
outputStream = new SenderOutputStream(sender);
|
||||
running = true;
|
||||
executor.execute(new Runnable() {
|
||||
public void run() {
|
||||
long now = System.currentTimeMillis();
|
||||
long next = now + TICK_INTERVAL;
|
||||
try {
|
||||
while(running) {
|
||||
byte[] b = null;
|
||||
while(now < next && b == null) {
|
||||
b = writes.poll(next - now, MILLISECONDS);
|
||||
now = System.currentTimeMillis();
|
||||
}
|
||||
if(b == null) {
|
||||
sender.tick();
|
||||
while(next <= now) next += TICK_INTERVAL;
|
||||
} else {
|
||||
if(b.length == 0) return; // Poison pill
|
||||
writeHandler.handleWrite(b);
|
||||
}
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.warning("Interrupted while waiting to write");
|
||||
Thread.currentThread().interrupt();
|
||||
running = false;
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
OutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
running = false;
|
||||
receiver.invalidate();
|
||||
writes.add(new byte[0]); // Poison pill
|
||||
}
|
||||
|
||||
// The modem calls this method to pass data up to the SLIP decoder
|
||||
public void handleRead(byte[] b) throws IOException {
|
||||
if(!running) throw new IOException("Connection closed");
|
||||
decoder.handleRead(b);
|
||||
}
|
||||
|
||||
// The SLIP encoder calls this method to pass data down to the modem
|
||||
public void handleWrite(byte[] b) throws IOException {
|
||||
if(!running) throw new IOException("Connection closed");
|
||||
if(b.length > 0) writes.add(b);
|
||||
}
|
||||
OutputStream getOutputStream();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
interface ReliabilityLayerFactory {
|
||||
|
||||
ReliabilityLayer createReliabilityLayer(WriteHandler writeHandler);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
class ReliabilityLayerFactoryImpl implements ReliabilityLayerFactory {
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
ReliabilityLayerFactoryImpl(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public ReliabilityLayer createReliabilityLayer(WriteHandler writeHandler) {
|
||||
return new ReliabilityLayerImpl(executor, writeHandler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
class ReliabilityLayerImpl implements ReliabilityLayer {
|
||||
|
||||
private static final int TICK_INTERVAL = 500; // Milliseconds
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ReliabilityLayerImpl.class.getName());
|
||||
|
||||
private final Executor executor;
|
||||
private final WriteHandler writeHandler;
|
||||
private final BlockingQueue<byte[]> writes;
|
||||
|
||||
private volatile Receiver receiver = null;
|
||||
private volatile SlipDecoder decoder = null;
|
||||
private volatile ReceiverInputStream inputStream = null;
|
||||
private volatile SenderOutputStream outputStream = null;
|
||||
private volatile boolean running = false;
|
||||
|
||||
ReliabilityLayerImpl(Executor executor, WriteHandler writeHandler) {
|
||||
this.executor = executor;
|
||||
this.writeHandler = writeHandler;
|
||||
writes = new LinkedBlockingQueue<byte[]>();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
SlipEncoder encoder = new SlipEncoder(this);
|
||||
final Sender sender = new Sender(encoder);
|
||||
receiver = new Receiver(sender);
|
||||
decoder = new SlipDecoder(receiver);
|
||||
inputStream = new ReceiverInputStream(receiver);
|
||||
outputStream = new SenderOutputStream(sender);
|
||||
running = true;
|
||||
executor.execute(new Runnable() {
|
||||
public void run() {
|
||||
long now = System.currentTimeMillis();
|
||||
long next = now + TICK_INTERVAL;
|
||||
try {
|
||||
while(running) {
|
||||
byte[] b = null;
|
||||
while(now < next && b == null) {
|
||||
b = writes.poll(next - now, MILLISECONDS);
|
||||
now = System.currentTimeMillis();
|
||||
}
|
||||
if(b == null) {
|
||||
sender.tick();
|
||||
while(next <= now) next += TICK_INTERVAL;
|
||||
} else {
|
||||
if(b.length == 0) return; // Poison pill
|
||||
writeHandler.handleWrite(b);
|
||||
}
|
||||
}
|
||||
} catch(InterruptedException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.warning("Interrupted while waiting to write");
|
||||
Thread.currentThread().interrupt();
|
||||
running = false;
|
||||
} catch(IOException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
receiver.invalidate();
|
||||
writes.add(new byte[0]); // Poison pill
|
||||
}
|
||||
|
||||
public InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
// The transport calls this method to pass data up to the SLIP decoder
|
||||
public void handleRead(byte[] b) throws IOException {
|
||||
if(!running) throw new IOException("Connection closed");
|
||||
decoder.handleRead(b);
|
||||
}
|
||||
|
||||
// The SLIP encoder calls this method to pass data down to the transport
|
||||
public void handleWrite(byte[] b) throws IOException {
|
||||
if(!running) throw new IOException("Connection closed");
|
||||
if(b.length > 0) writes.add(b);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HangupClientTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if(args.length != 2) {
|
||||
System.err.println("Please specify the server's phone number "
|
||||
+ " and the serial port");
|
||||
System.exit(1);
|
||||
}
|
||||
String number = args[0];
|
||||
String portName = args[1];
|
||||
Logger.getLogger("net.sf.briar").setLevel(INFO);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
Modem.Callback callback = new Modem.Callback() {
|
||||
public void incomingCallConnected() {
|
||||
System.err.println("Unexpected incoming call");
|
||||
System.exit(1);
|
||||
}
|
||||
};
|
||||
try {
|
||||
Modem modem = new ModemImpl(executor, callback, portName);
|
||||
modem.start();
|
||||
System.out.println("Dialling");
|
||||
if(modem.dial(number)) {
|
||||
System.out.println("Connected, waiting for server to hang up");
|
||||
Thread.sleep(60 * 1000);
|
||||
} else {
|
||||
System.out.println("Did not connect");
|
||||
}
|
||||
modem.stop();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package net.sf.briar.plugins.modem;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HangupServerTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if(args.length != 1) {
|
||||
System.err.println("Please specify the serial port");
|
||||
System.exit(1);
|
||||
}
|
||||
String portName = args[0];
|
||||
Logger.getLogger("net.sf.briar").setLevel(INFO);
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Modem.Callback callback = new Modem.Callback() {
|
||||
public void incomingCallConnected() {
|
||||
System.out.println("Connected");
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
try {
|
||||
final Modem modem = new ModemImpl(executor, callback, portName);
|
||||
modem.start();
|
||||
System.out.println("Waiting for incoming call");
|
||||
if(latch.await(60, SECONDS)) {
|
||||
System.out.println("Hanging up");
|
||||
modem.hangUp();
|
||||
} else {
|
||||
System.out.println("Did not connect");
|
||||
}
|
||||
modem.stop();
|
||||
} finally {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,8 @@ public class ModemClientTest extends DuplexClientTest {
|
||||
// Create the plugin
|
||||
callback = new ClientCallback(new TransportConfig(),
|
||||
new TransportProperties(), remote);
|
||||
plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor),
|
||||
callback, 0L);
|
||||
plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor,
|
||||
new ReliabilityLayerFactoryImpl(executor)), callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
@@ -21,8 +21,8 @@ public class ModemServerTest extends DuplexServerTest {
|
||||
callback = new ServerCallback(new TransportConfig(),
|
||||
new TransportProperties(), Collections.singletonMap(contactId,
|
||||
new TransportProperties()));
|
||||
plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor),
|
||||
callback, 0L);
|
||||
plugin = new ModemPlugin(executor, new ModemFactoryImpl(executor,
|
||||
new ReliabilityLayerFactoryImpl(executor)), callback, 0L);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user