Don't allow input or output streams to be used after hanging up.

This commit is contained in:
akwizgran
2012-11-26 22:26:07 +00:00
parent 4f37cb0837
commit f487d4f48a
2 changed files with 36 additions and 30 deletions

View File

@@ -6,20 +6,18 @@ import java.io.OutputStream;
/** /**
* A modem that can be used for multiple sequential incoming and outgoing * A modem that can be used for multiple sequential incoming and outgoing
* calls. * calls. If an exception is thrown, a new modem instance must be created.
*/ */
interface Modem { interface Modem {
/** /**
* Call this method after creating the modem and before making any calls. * Call this method after creating the modem and before making any calls.
* If an exception is thrown while using the modem, this method must be
* called again.
*/ */
void init() throws IOException; void init() throws IOException;
/** /**
* Initiates an outgoing call and returns true if the call connects. If the * Initiates an outgoing call and returns true if the call connects. If the
* call does not connect the modem is hung up and can continue to be used. * call does not connect the modem is hung up.
*/ */
boolean dial(String number) throws IOException; boolean dial(String number) throws IOException;

View File

@@ -40,7 +40,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
private int lineLen = 0; private int lineLen = 0;
private volatile BlockingQueue<byte[]> received = null; private volatile ModemInputStream inputStream = null;
private volatile ModemOutputStream outputStream = null;
ModemImpl(Executor executor, Callback callback, String portName) { ModemImpl(Executor executor, Callback callback, String portName) {
this.executor = executor; this.executor = executor;
@@ -55,10 +56,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
public void init() throws IOException { public void init() throws IOException {
if(LOG.isLoggable(INFO)) LOG.info("Initialising"); if(LOG.isLoggable(INFO)) LOG.info("Initialising");
try { try {
// Open the serial port
if(!port.openPort()) if(!port.openPort())
throw new IOException("Failed to open serial port"); throw new IOException("Failed to open serial port");
// Find a suitable baud rate
boolean foundBaudRate = false; boolean foundBaudRate = false;
for(int baudRate : BAUD_RATES) { for(int baudRate : BAUD_RATES) {
if(port.setParams(baudRate, 8, 1, 0)) { if(port.setParams(baudRate, 8, 1, 0)) {
@@ -68,9 +67,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
} }
if(!foundBaudRate) if(!foundBaudRate)
throw new IOException("Could not find a suitable baud rate"); throw new IOException("Could not find a suitable baud rate");
// Listen for incoming data and hangup events
port.addEventListener(this); port.addEventListener(this);
// Initialise the modem
port.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR); port.purgePort(PURGE_RXCLEAR | PURGE_TXCLEAR);
port.writeBytes("ATZ\r\n".getBytes("US-ASCII")); // Reset port.writeBytes("ATZ\r\n".getBytes("US-ASCII")); // Reset
port.writeBytes("ATE0\r\n".getBytes("US-ASCII")); // Echo off port.writeBytes("ATE0\r\n".getBytes("US-ASCII")); // Echo off
@@ -79,11 +76,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
throw new IOException(e.toString()); throw new IOException(e.toString());
} }
try { try {
// Wait for the modem to respond "OK"
synchronized(initialised) { synchronized(initialised) {
if(!initialised.get()) initialised.wait(OK_TIMEOUT); if(!initialised.get()) initialised.wait(OK_TIMEOUT);
if(!initialised.get())
throw new IOException("Modem did not respond");
} }
} catch(InterruptedException e) { } catch(InterruptedException e) {
if(LOG.isLoggable(WARNING)) if(LOG.isLoggable(WARNING))
@@ -92,7 +86,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new IOException("Interrupted while initialising modem"); throw new IOException("Interrupted while initialising modem");
} }
received = new LinkedBlockingQueue<byte[]>(); if(!initialised.get())
throw new IOException("Modem did not respond");
} }
public boolean dial(String number) throws IOException { public boolean dial(String number) throws IOException {
@@ -125,11 +120,11 @@ class ModemImpl implements Modem, SerialPortEventListener {
} }
public InputStream getInputStream() { public InputStream getInputStream() {
return new ModemInputStream(received); return inputStream;
} }
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
return new ModemOutputStream(); return outputStream;
} }
public void hangUp() throws IOException { public void hangUp() throws IOException {
@@ -140,8 +135,9 @@ class ModemImpl implements Modem, SerialPortEventListener {
tryToClose(port); tryToClose(port);
throw new IOException(e.toString()); throw new IOException(e.toString());
} }
received.add(new byte[0]); // Empty buffer indicates EOF inputStream.closed = true;
received = new LinkedBlockingQueue<byte[]>(); inputStream.received.add(new byte[0]); // Poison pill
outputStream.closed = true;
connected.set(false); connected.set(false);
offHook.release(); offHook.release();
} }
@@ -150,7 +146,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
try { try {
if(ev.isRXCHAR()) { if(ev.isRXCHAR()) {
byte[] b = port.readBytes(); byte[] b = port.readBytes();
if(connected.get()) received.add(b); if(connected.get()) inputStream.received.add(b);
else handleText(b); else handleText(b);
} else if(ev.isDSR() && ev.getEventValue() == 0) { } else if(ev.isDSR() && ev.getEventValue() == 0) {
if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up"); if(LOG.isLoggable(INFO)) LOG.info("Remote end hung up");
@@ -175,22 +171,26 @@ class ModemImpl implements Modem, SerialPortEventListener {
lineLen = 0; lineLen = 0;
if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s); if(LOG.isLoggable(INFO)) LOG.info("Modem status: " + s);
if(s.startsWith("CONNECT")) { if(s.startsWith("CONNECT")) {
inputStream = new ModemInputStream();
outputStream = new ModemOutputStream();
synchronized(connected) {
if(connected.getAndSet(true))
throw new IOException("Connected twice");
connected.notifyAll();
}
// There might be data in the buffer as well as text // There might be data in the buffer as well as text
int off = i + 1; int off = i + 1;
if(off < b.length) { if(off < b.length) {
byte[] data = new byte[b.length - off]; byte[] data = new byte[b.length - off];
System.arraycopy(b, off, data, 0, data.length); System.arraycopy(b, off, data, 0, data.length);
received.add(data); inputStream.received.add(data);
}
synchronized(connected) {
if(!connected.getAndSet(true))
connected.notifyAll();
} }
return; return;
} else if(s.equals("OK")) { } else if(s.equals("OK")) {
synchronized(initialised) { synchronized(initialised) {
if(!initialised.getAndSet(true)) if(initialised.getAndSet(true))
initialised.notifyAll(); throw new IOException("Initialised twice");
initialised.notifyAll();
} }
} else if(s.equals("RING")) { } else if(s.equals("RING")) {
executor.execute(new Runnable() { executor.execute(new Runnable() {
@@ -253,21 +253,23 @@ class ModemImpl implements Modem, SerialPortEventListener {
private byte[] buf = null; private byte[] buf = null;
private int offset = 0; private int offset = 0;
private ModemInputStream(BlockingQueue<byte[]> received) { private volatile boolean closed = false;
this.received = received;
private ModemInputStream() {
this.received = new LinkedBlockingQueue<byte[]>();
} }
@Override @Override
public int read() throws IOException { public int read() throws IOException {
if(closed) throw new IOException("Connection closed");
getBufferIfNecessary(); getBufferIfNecessary();
if(buf.length == 0) return -1;
return buf[offset++]; return buf[offset++];
} }
@Override @Override
public int read(byte[] b) throws IOException { public int read(byte[] b) throws IOException {
if(closed) throw new IOException("Connection closed");
getBufferIfNecessary(); getBufferIfNecessary();
if(buf.length == 0) return -1;
int len = Math.min(b.length, buf.length - offset); int len = Math.min(b.length, buf.length - offset);
System.arraycopy(buf, offset, b, 0, len); System.arraycopy(buf, offset, b, 0, len);
offset += len; offset += len;
@@ -276,8 +278,8 @@ class ModemImpl implements Modem, SerialPortEventListener {
@Override @Override
public int read(byte[] b, int off, int len) throws IOException { public int read(byte[] b, int off, int len) throws IOException {
if(closed) throw new IOException("Connection closed");
getBufferIfNecessary(); getBufferIfNecessary();
if(buf.length == 0) return -1;
len = Math.min(len, buf.length - offset); len = Math.min(len, buf.length - offset);
System.arraycopy(buf, offset, b, off, len); System.arraycopy(buf, offset, b, off, len);
offset += len; offset += len;
@@ -295,6 +297,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
throw new IOException(e.toString()); throw new IOException(e.toString());
} }
if(buf.length == 0) throw new IOException("Connection closed");
offset = 0; offset = 0;
} }
} }
@@ -302,8 +305,11 @@ class ModemImpl implements Modem, SerialPortEventListener {
private class ModemOutputStream extends OutputStream { private class ModemOutputStream extends OutputStream {
private volatile boolean closed = false;
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
if(closed) throw new IOException("Connection closed");
try { try {
port.writeByte((byte) b); port.writeByte((byte) b);
} catch(SerialPortException e) { } catch(SerialPortException e) {
@@ -314,6 +320,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
@Override @Override
public void write(byte[] b) throws IOException { public void write(byte[] b) throws IOException {
if(closed) throw new IOException("Connection closed");
try { try {
port.writeBytes(b); port.writeBytes(b);
} catch(SerialPortException e) { } catch(SerialPortException e) {
@@ -324,6 +331,7 @@ class ModemImpl implements Modem, SerialPortEventListener {
@Override @Override
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
if(closed) throw new IOException("Connection closed");
if(len < b.length) { if(len < b.length) {
byte[] copy = new byte[len]; byte[] copy = new byte[len];
System.arraycopy(b, off, copy, 0, len); System.arraycopy(b, off, copy, 0, len);