Files
j3270/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java
T
2026-08-28 12:38:56 -04:00

259 lines
9.8 KiB
Java

package haus.nightmare.lib3270j;
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
import haus.nightmare.lib3270j.datastream.DataStreamProcessor;
import haus.nightmare.lib3270j.input.InputProcessor;
import haus.nightmare.lib3270j.listener.ConnectionListener;
import haus.nightmare.lib3270j.listener.ScreenUpdateListener;
import haus.nightmare.lib3270j.screen.ScreenBuffer;
import haus.nightmare.lib3270j.telnet.TelnetConnection;
import haus.nightmare.lib3270j.telnet.TelnetFSM;
import java.io.IOException;
import java.util.logging.Logger;
/**
* Main API entry point for lib3270j.
*
* Provides a high-level interface for connecting to a TN3270 host,
* managing the screen buffer, and handling user input.
*
* Usage:
* <pre>
* ConnectionConfig config = new ConnectionConfig("hostname", 23, TerminalModel.IBM_3279_4);
* Telnet3270Client client = new Telnet3270Client(config);
* client.addConnectionListener(myListener);
* client.addScreenUpdateListener(myScreenListener);
* client.connect();
* // ... interact with screen ...
* client.disconnect();
* </pre>
*/
public class Telnet3270Client {
private static final Logger log = Logger.getLogger(Telnet3270Client.class.getName());
private final ConnectionConfig config;
private final EbcdicTranslator translator;
private final ScreenBuffer screenBuffer;
private final DataStreamProcessor dsProcessor;
private final TelnetFSM fsm;
private final InputProcessor inputProcessor;
private final haus.nightmare.lib3270j.ecl.ECLPS ps;
private final haus.nightmare.lib3270j.ecl.ECLOIA oia;
private TelnetConnection connection;
public Telnet3270Client(ConnectionConfig config) {
this.config = config;
this.translator = new EbcdicTranslator();
this.screenBuffer = new ScreenBuffer(config.getModel(), translator);
this.dsProcessor = new DataStreamProcessor(screenBuffer, translator);
this.dsProcessor.getQueryReplyBuilder().setGraphicsMode(config.getGraphicsMode());
this.fsm = new TelnetFSM(config, screenBuffer, dsProcessor);
this.inputProcessor = new InputProcessor(screenBuffer, translator, fsm);
this.ps = new haus.nightmare.lib3270j.ecl.ECLPS(screenBuffer, inputProcessor, translator);
this.oia = new haus.nightmare.lib3270j.ecl.ECLOIA(screenBuffer, inputProcessor, fsm);
// Wire up the output sender: DSProcessor -> FSM -> TelnetConnection
dsProcessor.setOutputSender(fsm::send3270Data);
dsProcessor.setInputProcessor(inputProcessor);
inputProcessor.setGraphicsPlane(dsProcessor.getGraphicsPlane());
inputProcessor.setGocaDecoder(dsProcessor.getGocaDecoder());
}
/**
* Connect to the configured host.
* This method blocks until the TCP connection is established,
* then returns while telnet/TN3270E negotiation continues asynchronously.
*/
public void connect() throws IOException {
log.info("Connecting to " + config.getHost() + ":" + config.getPort() +
" model=" + config.getModel());
connection = new TelnetConnection(config, fsm);
fsm.setConnection(connection);
// Establish TCP connection
connection.connect();
// Notify FSM that TCP is connected — begins telnet negotiation
fsm.onConnected();
}
/**
* Disconnect from the host.
*/
public void disconnect() {
if (connection != null) {
connection.disconnect();
connection = null;
}
fsm.onDisconnect();
}
/**
* Check if connected (any state past TCP pending).
*/
public boolean isConnected() {
return connection != null && connection.isConnected();
}
// ========== Listener management ==========
public void addConnectionListener(ConnectionListener l) {
fsm.addConnectionListener(l);
}
public void addScreenUpdateListener(ScreenUpdateListener l) {
fsm.addScreenUpdateListener(l);
dsProcessor.addScreenUpdateListener(l);
}
// ========== Screen access ==========
/** Get the screen buffer for rendering. */
public ScreenBuffer getScreenBuffer() { return screenBuffer; }
/** Get the EBCDIC translator. */
public EbcdicTranslator getTranslator() { return translator; }
/** Get the current connection state. */
public ConnectionState getConnectionState() { return fsm.getConnectionState(); }
/** Get the input processor for keyboard operations. */
public InputProcessor getInputProcessor() { return inputProcessor; }
/** Get the data stream processor. */
public DataStreamProcessor getDataStreamProcessor() { return dsProcessor; }
/** Get the connection config. */
public ConnectionConfig getConfig() { return config; }
/** Get the ECL Presentation Space API. */
public haus.nightmare.lib3270j.ecl.ECLPS getPS() { return ps; }
/** Get the ECL Operator Information Area API. */
public haus.nightmare.lib3270j.ecl.ECLOIA getOIA() { return oia; }
/** Get the list of all fields currently on screen. */
public haus.nightmare.lib3270j.ecl.ECLFieldList getFieldList() { return ps.getFieldList(); }
/** Send IBM ECL bracketed mnemonic keystrokes (e.g. "USER[tab]PASS[enter]"). */
public void sendKeys(String keys) { inputProcessor.sendKeys(keys); }
/** Set a custom or interactive TLS certificate verifier callback. */
public void setTlsCertificateVerifier(haus.nightmare.lib3270j.tls.TlsCertificateVerifier verifier) {
config.setCertificateVerifier(verifier);
}
/** Get the active SSLSession if connected over TLS, or null. */
public javax.net.ssl.SSLSession getSslSession() {
return connection.getSslSession();
}
// ========== Convenience input methods ==========
/** Type a character at the cursor position. */
public void typeCharacter(char ch) { inputProcessor.typeCharacter(ch); }
/** Type a string at the cursor position. */
public void typeString(String s) {
for (char ch : s.toCharArray()) {
inputProcessor.typeCharacter(ch);
}
}
/** Emulate input of a string, pressing Enter on newlines. */
public void emulateInput(String s) {
inputProcessor.emulateInput(s);
}
/** Send Enter key. */
public void sendEnter() {
inputProcessor.sendAid(haus.nightmare.lib3270j.protocol.DS3270Constants.AID_ENTER);
}
/** Send a PF key (1-24). */
public void sendPF(int number) {
int aid;
switch (number) {
case 1: aid = 0xf1; break; case 2: aid = 0xf2; break;
case 3: aid = 0xf3; break; case 4: aid = 0xf4; break;
case 5: aid = 0xf5; break; case 6: aid = 0xf6; break;
case 7: aid = 0xf7; break; case 8: aid = 0xf8; break;
case 9: aid = 0xf9; break; case 10: aid = 0x7a; break;
case 11: aid = 0x7b; break; case 12: aid = 0x7c; break;
case 13: aid = 0xc1; break; case 14: aid = 0xc2; break;
case 15: aid = 0xc3; break; case 16: aid = 0xc4; break;
case 17: aid = 0xc5; break; case 18: aid = 0xc6; break;
case 19: aid = 0xc7; break; case 20: aid = 0xc8; break;
case 21: aid = 0xc9; break; case 22: aid = 0x4a; break;
case 23: aid = 0x4b; break; case 24: aid = 0x4c; break;
default: return;
}
inputProcessor.sendAid(aid);
}
/** Send a PA key (1-3). */
public void sendPA(int number) {
int aid;
switch (number) {
case 1: aid = 0x6c; break;
case 2: aid = 0x6e; break;
case 3: aid = 0x6b; break;
default: return;
}
inputProcessor.sendAid(aid);
}
/** Send Clear key. */
public void sendClear() {
inputProcessor.sendAid(haus.nightmare.lib3270j.protocol.DS3270Constants.AID_CLEAR);
}
/** Move cursor up. */
public void cursorUp() { inputProcessor.cursorUp(); }
/** Move cursor down. */
public void cursorDown() { inputProcessor.cursorDown(); }
/** Move cursor left. */
public void cursorLeft() { inputProcessor.cursorLeft(); }
/** Move cursor right. */
public void cursorRight() { inputProcessor.cursorRight(); }
/** Move cursor to home position. */
public void cursorHome() { inputProcessor.cursorHome(); }
/** Tab to next unprotected field. */
public void tab() { inputProcessor.tab(); }
/** Back-tab to previous unprotected field. */
public void backTab() { inputProcessor.backTab(); }
/** Move cursor to next line. */
public void newline() { inputProcessor.newline(); }
/** Erase all unprotected fields. */
public void eraseInput() { inputProcessor.eraseInput(); }
/** Insert Duplicate order. */
public void dup() { inputProcessor.dup(); }
/** Insert Field Mark order. */
public void fieldMark() { inputProcessor.fieldMark(); }
/** Attention key. */
public void attn() { inputProcessor.attn(); }
/** SysReq key. */
public void sysReq() { inputProcessor.sysReq(); }
/** Reset (unlock keyboard). */
public void reset() { inputProcessor.reset(); }
/** Trigger 3270 CURSR SEL (Cursor Select) key at current cursor position. */
public boolean cursorSelect() { return inputProcessor.cursorSelect(); }
/** Trigger Light Pen selection at the specified screen address. */
public boolean lightPenSelect(int address) { return inputProcessor.lightPenSelect(address); }
public haus.nightmare.lib3270j.graphics.ProgramSymbolManager getProgramSymbolManager() {
return dsProcessor.getProgramSymbolManager();
}
public haus.nightmare.lib3270j.graphics.GraphicsPlane getGraphicsPlane() {
return dsProcessor.getGraphicsPlane();
}
public haus.nightmare.lib3270j.graphics.GocaDecoder getGocaDecoder() {
return dsProcessor.getGocaDecoder();
}
}