First pass at a modem plugin (unfinished).

This commit is contained in:
akwizgran
2012-11-24 13:35:23 +00:00
parent d706363866
commit 6cc8463209
5 changed files with 165 additions and 13 deletions

View File

@@ -6,7 +6,7 @@ import java.io.OutputStream;
/**
* A modem that can be used for multiple sequential incoming and outgoing
* calls. If the modem or its input or output streams throw an exception it
* calls. If the modem or its input or output streams throw any exceptions they
* cannot continue to be used.
*/
interface Modem {

View File

@@ -0,0 +1,6 @@
package net.sf.briar.plugins.modem;
interface ModemFactory {
Modem createModem(Modem.Callback callback, String portName);
}

View File

@@ -0,0 +1,16 @@
package net.sf.briar.plugins.modem;
import java.util.concurrent.Executor;
class ModemFactoryImpl implements ModemFactory {
private final Executor executor;
ModemFactoryImpl(Executor executor) {
this.executor = executor;
}
public Modem createModem(Modem.Callback callback, String portName) {
return new ModemImpl(executor, callback, portName);
}
}

View File

@@ -1,20 +1,34 @@
package net.sf.briar.plugins.modem;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import jssc.SerialPortList;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.crypto.PseudoRandom;
import net.sf.briar.api.plugins.PluginCallback;
import net.sf.briar.api.plugins.PluginExecutor;
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
import net.sf.briar.api.plugins.duplex.DuplexPluginCallback;
import net.sf.briar.api.plugins.duplex.DuplexTransportConnection;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.StringUtils;
class ModemPlugin implements DuplexPlugin {
class ModemPlugin implements DuplexPlugin, Modem.Callback {
static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("8f573867bedf54884b5868ee5d902832" +
@@ -26,14 +40,22 @@ class ModemPlugin implements DuplexPlugin {
Logger.getLogger(ModemPlugin.class.getName());
private final Executor pluginExecutor;
private final PluginCallback callback;
private final ModemFactory modemFactory;
private final DuplexPluginCallback callback;
private final long pollingInterval;
private final Semaphore polling;
private volatile boolean running = false;
private volatile Modem modem = null;
ModemPlugin(@PluginExecutor Executor pluginExecutor,
PluginCallback callback, long pollingInterval) {
ModemFactory modemFactory, DuplexPluginCallback callback,
long pollingInterval) {
this.pluginExecutor = pluginExecutor;
this.modemFactory = modemFactory;
this.callback = callback;
this.pollingInterval = pollingInterval;
polling = new Semaphore(1);
}
public TransportId getId() {
@@ -44,13 +66,22 @@ class ModemPlugin implements DuplexPlugin {
return "MODEM_PLUGIN_NAME";
}
public boolean start() throws IOException {
// FIXME
public boolean start() {
for(String portName : SerialPortList.getPortNames()) {
modem = modemFactory.createModem(this, portName);
try {
modem.init();
running = true;
return true;
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
return false;
}
public void stop() throws IOException {
// FIXME
public void stop() {
running = false;
}
public boolean shouldPoll() {
@@ -62,12 +93,68 @@ class ModemPlugin implements DuplexPlugin {
}
public void poll(Collection<ContactId> connected) {
// FIXME
if(!connected.isEmpty()) return; // One at a time please
pluginExecutor.execute(new Runnable() {
public void run() {
poll();
}
});
}
private void poll() {
if(!running) return;
if(!polling.tryAcquire()) {
if(LOG.isLoggable(INFO))
LOG.info("Previous poll still in progress");
return;
}
// Call contacts one at a time in a random order
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
List<ContactId> contacts = new ArrayList<ContactId>(remote.keySet());
Collections.shuffle(contacts);
Iterator<ContactId> it = contacts.iterator();
while(it.hasNext() && running) {
ContactId c = it.next();
String number = remote.get(c).get("number");
if(number == null) continue;
try {
if(!modem.dial(number)) continue;
} catch(IOException e) {
// FIXME: Race condition with stop()
running = false;
if(start()) continue;
else break;
}
ModemTransportConnection conn = new ModemTransportConnection();
callback.outgoingConnectionCreated(c, conn);
try {
conn.waitForDisposal();
} catch(InterruptedException e) {
if(LOG.isLoggable(WARNING))
LOG.warning("Interrupted while polling");
Thread.currentThread().interrupt();
break;
}
}
polling.release();
}
public DuplexTransportConnection createConnection(ContactId c) {
// FIXME
return null;
if(!running) return null;
final Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
String number = remote.get(c).get("number");
if(number == null) return null;
try {
if(!modem.dial(number)) return null;
} catch(IOException e) {
// FIXME: Race condition with stop()
running = false;
start();
return null;
}
return new ModemTransportConnection();
}
public boolean supportsInvitations() {
@@ -83,4 +170,45 @@ class ModemPlugin implements DuplexPlugin {
long timeout) {
throw new UnsupportedOperationException();
}
public void incomingCallConnected() {
callback.incomingConnectionCreated(new ModemTransportConnection());
}
private class ModemTransportConnection
implements DuplexTransportConnection {
private final CountDownLatch finished = new CountDownLatch(1);
public InputStream getInputStream() {
return modem.getInputStream();
}
public OutputStream getOutputStream() {
return modem.getOutputStream();
}
public boolean shouldFlush() {
return true;
}
public void dispose(boolean exception, boolean recognised) {
try {
modem.hangUp();
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
exception = true;
}
if(exception) {
// FIXME: Race condition with stop()
running = false;
start();
}
finished.countDown();
}
private void waitForDisposal() throws InterruptedException {
finished.await();
}
}
}

View File

@@ -28,6 +28,8 @@ public class ModemPluginFactory implements DuplexPluginFactory {
// This plugin is not enabled by default
String enabled = callback.getConfig().get("enabled");
if(StringUtils.isNullOrEmpty(enabled)) return null;
return new ModemPlugin(pluginExecutor, callback, POLLING_INTERVAL);
ModemFactory modemFactory = new ModemFactoryImpl(pluginExecutor);
return new ModemPlugin(pluginExecutor, modemFactory, callback,
POLLING_INTERVAL);
}
}