259 lines
9.8 KiB
Java
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();
|
|
}
|
|
}
|