Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better handling of closed stream errors #1304

Merged
merged 11 commits into from
Dec 3, 2024
5 changes: 2 additions & 3 deletions src/qz/communication/DeviceIO.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package qz.communication;

public interface DeviceIO {
public interface DeviceIO extends DeviceListener {

String getVendorId();

Expand All @@ -11,8 +11,7 @@ public interface DeviceIO {

boolean isOpen();

void close() throws DeviceException;

void close();

void setStreaming(boolean streaming);

Expand Down
4 changes: 3 additions & 1 deletion src/qz/communication/DeviceListener.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package qz.communication;

public interface DeviceListener {

/**
* Cleanup task for when a socket closes while a device is still streaming
*/
void close();

}
8 changes: 5 additions & 3 deletions src/qz/communication/FileIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
import qz.ws.PrintSocketClient;
import qz.ws.StreamEvent;

import java.nio.channels.ClosedChannelException;
import java.nio.file.Path;
import java.nio.file.WatchKey;
import java.util.ArrayList;

public class FileIO {
public class FileIO implements DeviceListener {
public static final String SANDBOX_DATA_SUFFIX = "sandbox";
public static final String GLOBAL_DATA_SUFFIX = "shared";
public static final int FILE_LISTENER_DEFAULT_LINES = 10;
Expand Down Expand Up @@ -133,7 +134,7 @@ public void setWk(WatchKey wk) {
this.wk = wk;
}

public void fileChanged(String fileName, String type, String fileData) {
public void fileChanged(String fileName, String type, String fileData) throws ClosedChannelException {
StreamEvent evt = new StreamEvent(StreamEvent.Stream.FILE, StreamEvent.Type.ACTION)
.withData("file", getOriginalPath().resolve(fileName))
.withData("eventType", type);
Expand All @@ -145,13 +146,14 @@ public void fileChanged(String fileName, String type, String fileData) {
PrintSocketClient.sendStream(session, evt);
}

public void sendError(String message) {
public void sendError(String message) throws ClosedChannelException {
StreamEvent eventErr = new StreamEvent(StreamEvent.Stream.FILE, StreamEvent.Type.ERROR)
.withData("message", message);
PrintSocketClient.sendStream(session, eventErr);
}


@Override
public void close() {
if (wk != null) {
wk.cancel();
Expand Down
21 changes: 15 additions & 6 deletions src/qz/communication/H4J_HidIO.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
package qz.communication;

import org.hid4java.HidDevice;
import qz.ws.SocketConnection;

import javax.usb.util.UsbUtil;

public class H4J_HidIO implements DeviceIO {
public class H4J_HidIO implements DeviceIO, DeviceListener {

private HidDevice device;

private boolean streaming;

private DeviceOptions dOpts;
private SocketConnection websocket;

public H4J_HidIO(DeviceOptions dOpts) throws DeviceException {
this(H4J_HidUtilities.findDevice(dOpts));

public H4J_HidIO(DeviceOptions dOpts, SocketConnection websocket) throws DeviceException {
this(H4J_HidUtilities.findDevice(dOpts), dOpts, websocket);
}

public H4J_HidIO(HidDevice device) throws DeviceException {
private H4J_HidIO(HidDevice device, DeviceOptions dOpts, SocketConnection websocket) throws DeviceException {
this.dOpts = dOpts;
this.websocket = websocket;
if (device == null) {
throw new DeviceException("HID device could not be found");
}
Expand All @@ -30,7 +36,7 @@ public void open() {
}

public boolean isOpen() {
return device.isOpen();
return !device.isClosed();
}

public void setStreaming(boolean active) {
Expand Down Expand Up @@ -91,11 +97,14 @@ public void sendFeatureReport(byte[] data, Byte reportId) throws DeviceException
}
}

@Override
public void close() {
setStreaming(false);
// Remove orphaned reference
websocket.removeDevice(dOpts);
if (isOpen()) {
device.close();
}
streaming = false;
}

}
8 changes: 4 additions & 4 deletions src/qz/communication/H4J_HidListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public H4J_HidListener(Session session) {
@Override
public void hidFailure(HidServicesEvent hidServicesEvent) {
log.debug("Device failure: {}", hidServicesEvent.getHidDevice().getProduct());
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Failure"));
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Failure"), this);
}

@Override
Expand All @@ -42,19 +42,19 @@ public void hidDataReceived(HidServicesEvent hidServicesEvent) {
hex.put(UsbUtil.toHexString(b));
}

PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Data Received", hex));
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Data Received", hex), this);
}

@Override
public void hidDeviceDetached(HidServicesEvent hidServicesEvent) {
log.debug("Device detached: {}", hidServicesEvent.getHidDevice().getProduct());
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Detached"));
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Detached"), this);
}

@Override
public void hidDeviceAttached(HidServicesEvent hidServicesEvent) {
log.debug("Device attached: {}", hidServicesEvent.getHidDevice().getProduct());
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Attached"));
PrintSocketClient.sendStream(session, createStreamAction(hidServicesEvent.getHidDevice(), "Device Attached"), this);
}

private StreamEvent createStreamAction(HidDevice device, String action) {
Expand Down
17 changes: 12 additions & 5 deletions src/qz/communication/PJHA_HidIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import purejavahidapi.InputReportListener;
import purejavahidapi.PureJavaHidApi;
import qz.utils.SystemUtilities;
import qz.ws.SocketConnection;

import javax.usb.util.UsbUtil;
import java.io.IOException;
Expand All @@ -22,13 +23,16 @@ public class PJHA_HidIO implements DeviceIO {
private static final int BUFFER_SIZE = 32;
private Vector<byte[]> dataBuffer;
private boolean streaming;
private DeviceOptions dOpts;
private SocketConnection websocket;


public PJHA_HidIO(DeviceOptions dOpts) throws DeviceException {
this(PJHA_HidUtilities.findDevice(dOpts));
public PJHA_HidIO(DeviceOptions dOpts, SocketConnection websocket) throws DeviceException {
this(PJHA_HidUtilities.findDevice(dOpts), dOpts, websocket);
}

public PJHA_HidIO(HidDeviceInfo deviceInfo) throws DeviceException {
private PJHA_HidIO(HidDeviceInfo deviceInfo, DeviceOptions dOpts, SocketConnection websocket) throws DeviceException {
this.dOpts = dOpts;
this.websocket = websocket;
if (deviceInfo == null) {
throw new DeviceException("HID device could not be found");
}
Expand Down Expand Up @@ -129,7 +133,11 @@ public void sendFeatureReport(byte[] data, Byte reportId) throws DeviceException

}

@Override
public void close() {
setStreaming(false);
// Remove orphaned reference
websocket.removeDevice(dOpts);
if (isOpen()) {
try {
device.setInputReportListener(null);
Expand All @@ -140,7 +148,6 @@ public void close() {
}
}

streaming = false;
device = null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/qz/communication/PJHA_HidListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,6 @@ public void close() {
@Override
public void onDeviceRemoval(HidDevice device) {
log.debug("Device detached: {}", device.getHidDeviceInfo().getProductString());
PrintSocketClient.sendStream(session, createStreamAction(device, "Device Detached"));
PrintSocketClient.sendStream(session, createStreamAction(device, "Device Detached"), this);
}
}
31 changes: 20 additions & 11 deletions src/qz/communication/SerialIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
import qz.common.ByteArrayBuilder;
import qz.utils.ByteUtilities;
import qz.utils.DeviceUtilities;
import qz.ws.SocketConnection;

import java.io.IOException;

/**
* @author Tres
*/
public class SerialIO {
public class SerialIO implements DeviceListener {

private static final Logger log = LogManager.getLogger(SerialIO.class);

Expand All @@ -28,14 +29,17 @@ public class SerialIO {

private ByteArrayBuilder data = new ByteArrayBuilder();

private SocketConnection websocket;


/**
* Controller for serial communications
*
* @param portName Port name to open, such as "COM1" or "/dev/tty0/"
*/
public SerialIO(String portName) {
public SerialIO(String portName, SocketConnection websocket) {
this.portName = portName;
this.websocket = websocket;
}

/**
Expand Down Expand Up @@ -257,26 +261,31 @@ public void sendData(JSONObject params, SerialOptions opts) throws JSONException
/**
* Closes the serial port, if open.
*
* @return Boolean indicating success.
* @throws SerialPortException If the port fails to close.
*/
public boolean close() throws SerialPortException {
@Override
public void close() {
// Remove orphaned reference
websocket.removeSerialPort(portName);

if (!isOpen()) {
log.warn("Serial port [{}] is not open.", portName);
return false;
}

boolean closed = port.closePort();
if (closed) {
log.info("Serial port [{}] closed successfully.", portName);
} else {
try {
boolean closed = port.closePort();
if (closed) {
log.info("Serial port [{}] closed successfully.", portName);
} else {
// Handle ambiguity in JSSCs API
throw new SerialPortException(portName, "closePort", "Port not closed");
}
} catch(SerialPortException e) {
log.warn("Serial port [{}] was not closed properly.", portName);
}

port = null;
portName = null;

return closed;
}

private Integer min(Integer a, Integer b) {
Expand Down
26 changes: 21 additions & 5 deletions src/qz/communication/SocketIO.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.apache.logging.log4j.Logger;
import qz.utils.DeviceUtilities;
import qz.utils.NetworkUtilities;
import qz.ws.SocketConnection;

import java.io.DataInputStream;
import java.io.DataOutputStream;
Expand All @@ -15,7 +16,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;

public class SocketIO {
public class SocketIO implements DeviceListener {

private static final Logger log = LogManager.getLogger(SocketIO.class);

Expand All @@ -27,10 +28,13 @@ public class SocketIO {
private DataOutputStream dataOut;
private DataInputStream dataIn;

public SocketIO(String host, int port, Charset encoding) {
private SocketConnection websocket;

public SocketIO(String host, int port, Charset encoding, SocketConnection websocket) {
this.host = host;
this.port = port;
this.encoding = encoding;
this.websocket = websocket;
}

public boolean open() throws IOException {
Expand Down Expand Up @@ -68,9 +72,21 @@ public String processSocketResponse() throws IOException {
return null;
}

public void close() throws IOException {
dataOut.close();
socket.close();
@Override
public void close() {
// Remove orphaned reference
websocket.removeNetworkSocket(String.format("%s:%s", host, port));

try {
dataOut.close();
} catch(IOException e) {
log.warn("Could not close socket output stream", e);
}
try {
socket.close();
} catch(IOException e) {
log.warn("Could not close socket", e);
}
}

public String getHost() {
Expand Down
Loading
Loading