Add Read Modified Query from Moshix bug report
Build and Test j3270 / Build JAR & Run Tests (Java 11) (push) Successful in 1m19s
Build and Test j3270 / Build JAR & Run Tests (Java 21) (push) Successful in 1m24s
Build and Test j3270 / Build JAR & Run Tests (Java 17) (push) Successful in 1m43s
Release j3270 / Build & Publish Release (push) Successful in 39s
Build and Test j3270 / Build JAR & Run Tests (Java 11) (push) Successful in 1m19s
Build and Test j3270 / Build JAR & Run Tests (Java 21) (push) Successful in 1m24s
Build and Test j3270 / Build JAR & Run Tests (Java 17) (push) Successful in 1m43s
Release j3270 / Build & Publish Release (push) Successful in 39s
This commit is contained in:
+48
-10
@@ -53,6 +53,7 @@ public class DataStreamProcessor {
|
|||||||
private boolean unlockPending = false;
|
private boolean unlockPending = false;
|
||||||
private boolean unlockSysPending = false;
|
private boolean unlockSysPending = false;
|
||||||
private boolean rcvdRead = false;
|
private boolean rcvdRead = false;
|
||||||
|
private boolean qreplyPending = false;
|
||||||
|
|
||||||
// Modal SA (set attribute) character attributes
|
// Modal SA (set attribute) character attributes
|
||||||
private byte currentFg = 0;
|
private byte currentFg = 0;
|
||||||
@@ -183,11 +184,13 @@ public class DataStreamProcessor {
|
|||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CMD_W:
|
case CMD_W:
|
||||||
case SNA_CMD_W:
|
case SNA_CMD_W:
|
||||||
|
qreplyPending = false;
|
||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
processWrite(data, offset, length, false);
|
processWrite(data, offset, length, false);
|
||||||
break;
|
break;
|
||||||
case CMD_EW:
|
case CMD_EW:
|
||||||
case SNA_CMD_EW:
|
case SNA_CMD_EW:
|
||||||
|
qreplyPending = false;
|
||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
if (log.isLoggable(Level.FINE)) {
|
if (log.isLoggable(Level.FINE)) {
|
||||||
log.fine(">>> ERASE/WRITE: clearing screen (default size)");
|
log.fine(">>> ERASE/WRITE: clearing screen (default size)");
|
||||||
@@ -208,6 +211,7 @@ public class DataStreamProcessor {
|
|||||||
break;
|
break;
|
||||||
case CMD_EWA:
|
case CMD_EWA:
|
||||||
case SNA_CMD_EWA:
|
case SNA_CMD_EWA:
|
||||||
|
qreplyPending = false;
|
||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
if (log.isLoggable(Level.FINE)) {
|
if (log.isLoggable(Level.FINE)) {
|
||||||
log.fine(">>> ERASE/WRITE ALTERNATE: clearing screen (alt size)");
|
log.fine(">>> ERASE/WRITE ALTERNATE: clearing screen (alt size)");
|
||||||
@@ -236,7 +240,12 @@ public class DataStreamProcessor {
|
|||||||
case SNA_CMD_RM:
|
case SNA_CMD_RM:
|
||||||
rcvdRead = true;
|
rcvdRead = true;
|
||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
|
if (qreplyPending) {
|
||||||
|
qreplyPending = false;
|
||||||
|
sendAllQueryReplies();
|
||||||
|
} else {
|
||||||
processReadModified(false);
|
processReadModified(false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CMD_RMA:
|
case CMD_RMA:
|
||||||
case SNA_CMD_RMA:
|
case SNA_CMD_RMA:
|
||||||
@@ -246,6 +255,7 @@ public class DataStreamProcessor {
|
|||||||
break;
|
break;
|
||||||
case CMD_EAU:
|
case CMD_EAU:
|
||||||
case SNA_CMD_EAU:
|
case SNA_CMD_EAU:
|
||||||
|
qreplyPending = false;
|
||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
log.info(">>> EAU: erasing all unprotected fields");
|
log.info(">>> EAU: erasing all unprotected fields");
|
||||||
screen.eraseAllUnprotected();
|
screen.eraseAllUnprotected();
|
||||||
@@ -924,13 +934,26 @@ public class DataStreamProcessor {
|
|||||||
// ========== Read Buffer ==========
|
// ========== Read Buffer ==========
|
||||||
|
|
||||||
public void processReadBuffer() {
|
public void processReadBuffer() {
|
||||||
|
int aid = (inputProcessor != null && inputProcessor.getLastAid() != 0)
|
||||||
|
? inputProcessor.getLastAid()
|
||||||
|
: AID_NO;
|
||||||
|
processReadBuffer(aid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processReadBuffer(int aid) {
|
||||||
|
if (inputProcessor != null) {
|
||||||
|
inputProcessor.setLastAid(AID_NO);
|
||||||
|
byte[] data = inputProcessor.buildReadBufferInboundData(aid);
|
||||||
|
if (outputSender != null) {
|
||||||
|
outputSender.send3270Data(data);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
outputPos = 0;
|
outputPos = 0;
|
||||||
|
|
||||||
int size = screen.getRows() * screen.getCols();
|
int size = screen.getRows() * screen.getCols();
|
||||||
|
|
||||||
int aid = (inputProcessor != null && inputProcessor.getLastAid() != 0)
|
|
||||||
? inputProcessor.getLastAid()
|
|
||||||
: AID_NO;
|
|
||||||
if (inputProcessor != null) {
|
if (inputProcessor != null) {
|
||||||
inputProcessor.setLastAid(AID_NO);
|
inputProcessor.setLastAid(AID_NO);
|
||||||
}
|
}
|
||||||
@@ -1030,12 +1053,18 @@ public class DataStreamProcessor {
|
|||||||
// ========== Read Modified ==========
|
// ========== Read Modified ==========
|
||||||
|
|
||||||
public void processReadModified(boolean all) {
|
public void processReadModified(boolean all) {
|
||||||
|
int aid = (inputProcessor != null && inputProcessor.getLastAid() != 0)
|
||||||
|
? inputProcessor.getLastAid()
|
||||||
|
: AID_NO;
|
||||||
|
processReadModified(aid, all);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void processReadModified(int aid, boolean all) {
|
||||||
if (ftDft != null && ftDft.readModified()) {
|
if (ftDft != null && ftDft.readModified()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputProcessor != null) {
|
if (inputProcessor != null) {
|
||||||
int aid = (inputProcessor.getLastAid() != 0) ? inputProcessor.getLastAid() : AID_NO;
|
|
||||||
inputProcessor.setLastAid(AID_NO);
|
inputProcessor.setLastAid(AID_NO);
|
||||||
byte[] data = inputProcessor.buildReadModifiedInboundData(aid, all);
|
byte[] data = inputProcessor.buildReadModifiedInboundData(aid, all);
|
||||||
if (outputSender != null) {
|
if (outputSender != null) {
|
||||||
@@ -1046,9 +1075,6 @@ public class DataStreamProcessor {
|
|||||||
|
|
||||||
outputPos = 0;
|
outputPos = 0;
|
||||||
|
|
||||||
int aid = (inputProcessor != null && inputProcessor.getLastAid() != 0)
|
|
||||||
? inputProcessor.getLastAid()
|
|
||||||
: AID_NO;
|
|
||||||
if (inputProcessor != null) {
|
if (inputProcessor != null) {
|
||||||
inputProcessor.setLastAid(AID_NO);
|
inputProcessor.setLastAid(AID_NO);
|
||||||
}
|
}
|
||||||
@@ -1211,9 +1237,11 @@ public class DataStreamProcessor {
|
|||||||
log.info("ReadPartition Query — sending base query replies");
|
log.info("ReadPartition Query — sending base query replies");
|
||||||
graphicsPlane.clear();
|
graphicsPlane.clear();
|
||||||
gocaDecoder.resetDefaults();
|
gocaDecoder.resetDefaults();
|
||||||
|
qreplyPending = true;
|
||||||
sendAllQueryReplies();
|
sendAllQueryReplies();
|
||||||
break;
|
break;
|
||||||
case SF_RP_QLIST:
|
case SF_RP_QLIST:
|
||||||
|
qreplyPending = true;
|
||||||
if (fieldLen >= 6) {
|
if (fieldLen >= 6) {
|
||||||
int listType = data[offset + 5] & 0xFF;
|
int listType = data[offset + 5] & 0xFF;
|
||||||
log.info("ReadPartition QueryList type=" + String.format("0x%02x", listType));
|
log.info("ReadPartition QueryList type=" + String.format("0x%02x", listType));
|
||||||
@@ -1236,14 +1264,16 @@ public class DataStreamProcessor {
|
|||||||
sendAllQueryReplies();
|
sendAllQueryReplies();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case CMD_RMA:
|
||||||
case SNA_CMD_RMA:
|
case SNA_CMD_RMA:
|
||||||
processReadModified(true);
|
processReadModified(AID_QREPLY, true);
|
||||||
break;
|
break;
|
||||||
case SNA_CMD_RB:
|
case SNA_CMD_RB:
|
||||||
processReadBuffer();
|
processReadBuffer(AID_QREPLY);
|
||||||
break;
|
break;
|
||||||
|
case CMD_RM:
|
||||||
case SNA_CMD_RM:
|
case SNA_CMD_RM:
|
||||||
processReadModified(false);
|
processReadModified(AID_QREPLY, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log.fine("Unknown ReadPartition type: " + String.format("0x%02x", type));
|
log.fine("Unknown ReadPartition type: " + String.format("0x%02x", type));
|
||||||
@@ -1674,6 +1704,14 @@ public class DataStreamProcessor {
|
|||||||
processReadModified(true);
|
processReadModified(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isQreplyPending() {
|
||||||
|
return qreplyPending;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQreplyPending(boolean qreplyPending) {
|
||||||
|
this.qreplyPending = qreplyPending;
|
||||||
|
}
|
||||||
|
|
||||||
public void processWriteStructuredField(byte[] data) {
|
public void processWriteStructuredField(byte[] data) {
|
||||||
if (data != null && data.length > 0) {
|
if (data != null && data.length > 0) {
|
||||||
processWriteStructuredField(data, 0, data.length);
|
processWriteStructuredField(data, 0, data.length);
|
||||||
|
|||||||
+87
@@ -271,4 +271,91 @@ public class DataStreamProcessorTest {
|
|||||||
assertEquals('8', screen.getCell(12).ucs4);
|
assertEquals('8', screen.getCell(12).ucs4);
|
||||||
assertEquals('9', screen.getCell(13).ucs4);
|
assertEquals('9', screen.getCell(13).ucs4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadPartitionReadModifiedReturnsAidQreply() {
|
||||||
|
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
|
||||||
|
processor.setOutputSender(out::writeBytes);
|
||||||
|
|
||||||
|
// Send WSF Read Partition with SNA_CMD_RM (0xF6)
|
||||||
|
byte[] sfRm = new byte[] {
|
||||||
|
(byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, 0x00, (byte) SNA_CMD_RM
|
||||||
|
};
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(sfRm, 0, sfRm.length, true);
|
||||||
|
|
||||||
|
byte[] sent = out.toByteArray();
|
||||||
|
assertTrue(sent.length >= 3, "Inbound data stream must contain at least AID and cursor address");
|
||||||
|
assertEquals((byte) AID_QREPLY, sent[0], "Read Partition Read Modified AID must be AID_QREPLY (0x61)");
|
||||||
|
|
||||||
|
// Send WSF Read Partition with CMD_RM (0x06)
|
||||||
|
byte[] sfRmNonSna = new byte[] {
|
||||||
|
(byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, 0x00, (byte) CMD_RM
|
||||||
|
};
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(sfRmNonSna, 0, sfRmNonSna.length, true);
|
||||||
|
|
||||||
|
sent = out.toByteArray();
|
||||||
|
assertTrue(sent.length >= 3);
|
||||||
|
assertEquals((byte) AID_QREPLY, sent[0], "Read Partition Read Modified (non-SNA) AID must be AID_QREPLY (0x61)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadPartitionReadModifiedAllReturnsAidQreply() {
|
||||||
|
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
|
||||||
|
processor.setOutputSender(out::writeBytes);
|
||||||
|
|
||||||
|
// Send WSF Read Partition with SNA_CMD_RMA (0x6E)
|
||||||
|
byte[] sfRma = new byte[] {
|
||||||
|
(byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, 0x00, (byte) SNA_CMD_RMA
|
||||||
|
};
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(sfRma, 0, sfRma.length, true);
|
||||||
|
|
||||||
|
byte[] sent = out.toByteArray();
|
||||||
|
assertTrue(sent.length >= 3);
|
||||||
|
assertEquals((byte) AID_QREPLY, sent[0], "Read Partition Read Modified All AID must be AID_QREPLY (0x61)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadPartitionReadBufferReturnsAidQreply() {
|
||||||
|
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
|
||||||
|
processor.setOutputSender(out::writeBytes);
|
||||||
|
|
||||||
|
// Send WSF Read Partition with SNA_CMD_RB (0xF2)
|
||||||
|
byte[] sfRb = new byte[] {
|
||||||
|
(byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, 0x00, (byte) SNA_CMD_RB
|
||||||
|
};
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(sfRb, 0, sfRb.length, true);
|
||||||
|
|
||||||
|
byte[] sent = out.toByteArray();
|
||||||
|
assertTrue(sent.length >= 3);
|
||||||
|
assertEquals((byte) AID_QREPLY, sent[0], "Read Partition Read Buffer AID must be AID_QREPLY (0x61)");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadModifiedAfterQueryRespondsWithQueryReply() {
|
||||||
|
java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
|
||||||
|
processor.setOutputSender(out::writeBytes);
|
||||||
|
|
||||||
|
// 1. Send WSF Read Partition Query
|
||||||
|
byte[] sfQuery = new byte[] {
|
||||||
|
(byte) CMD_WSF, 0x00, 0x05, (byte) SF_READ_PART, (byte) 0xFF, (byte) SF_RP_QUERY
|
||||||
|
};
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(sfQuery, 0, sfQuery.length, true);
|
||||||
|
|
||||||
|
assertTrue(processor.isQreplyPending(), "qreplyPending should be true after query");
|
||||||
|
|
||||||
|
// 2. Mainframe issues channel Read Modified command (0x06) to poll the query reply
|
||||||
|
byte[] rmCmd = new byte[] { (byte) CMD_RM };
|
||||||
|
out.reset();
|
||||||
|
processor.processRecord(rmCmd, 0, rmCmd.length, true);
|
||||||
|
|
||||||
|
byte[] sent = out.toByteArray();
|
||||||
|
assertTrue(sent.length > 0, "Should have received query reply data");
|
||||||
|
assertEquals((byte) AID_SF, sent[0], "Query reply header AID must be AID_SF (0x88)");
|
||||||
|
assertFalse(processor.isQreplyPending(), "qreplyPending should be cleared after reply is sent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user