Class HostSession

java.lang.Object
com.iizix.term.HostSession

public class HostSession extends Object
The 3270 or 5250 (or EE) host session.

Threading contract

The lock for ALL session and screen-mirror state is the SESSION MONITOR: synchronized on this HostSession instance. The peers (3270/5250/EE) take the same monitor as synchronized(owner) on the host-driven path, and every mutating entry point of this class is a synchronized method - the key-driven and host-driven paths serialise on one lock. There is no other lock in this domain: HostScreen has no monitor of its own.

Safe from any thread

Every public synchronized method: the outbound writers (sendString, sendCharacterString, sendKey, setHostString, setHostStringAsKeys, setCursor, setInsertMode, clearTypeAhead, doesHostNeedFieldExit), the compound helpers (refreshHostFields, getCharactersAndAttributes, performCopy, performClear, performPaste) and the lifecycle methods (connect, disconnect, performSuspend, performResume). Each single call is atomic with respect to the host thread.

Multi-call sequences are NOT atomic

The host thread can process a record between any two calls. A caller that needs a read-then-write or a consistent compound read across several calls must hold the session monitor across the whole sequence:
  synchronized(hostSession)
    {
    if ( hostSession.doesHostNeedFieldExit(x,y,0,len,text) )
      hostSession.setHostStringAsKeys(x,y,0,len,text);
    else
      hostSession.setHostString(x,y,0,len,text);
    }
This is safe from any thread, the SWT thread included: no code path holding this monitor blocks waiting for another thread (in particular, no Display.syncExec runs under it), and the monitor is reentrant from the synchronized entry points and listener callbacks.

Whole-screen snapshot

snapshot() returns a consistent, immutable copy of the screen mirror (characters, attributes, size, cursor, fields) without the caller needing the monitor - built for the MCP/out-of-process boundary. In-process callers that can hold the session monitor across their reads should do that instead; see the snapshot() Javadoc for what consistent does and does not mean (in particular: consistent is NOT settled).

Listener rules

HostSessionListener and HostScreenListener callbacks are invoked WHILE the session monitor is held. Listener code must not block waiting for a thread that may itself need this monitor (no Display.syncExec), and must not acquire the property tree lock.

Deliberately unsynchronized readers

The simple state readers (isConnected, isConnecting, isDisconnected, isReconnecting, isSuspended, isLocked, hasError, inInsertMode, getSessionState, getCursor, getLastError, getDeviceName, getHostScreenSize, getHostScreenLength, isFieldFormatted, isProcessingDataStream, getInfo) intentionally do NOT take the session monitor. They read a single plain field and can return a STALE value but never a torn one. DO NOT "fix" this by adding synchronized to them. RemoteHostSession.onPreSend calls getSessionState/getLastError/getDeviceName while the property TREE LOCK is held (PropMgr.getSendTransaction serialisation); synchronizing these readers would create the lock order treeLock -> session monitor. That order is acyclic today, but only because nothing acquires the tree lock while holding a session monitor - a rule that exists nowhere else and would then have to be enforced forever. Keeping these readers lock-free keeps the two lock domains fully independent. A caller that needs a CONSISTENT compound view must hold the session monitor across the calls, as described above.

This is a reviewed, FINAL decision (G5a of the terminal locking programme, 2026-08-17), not a provisional state awaiting "completion": the readers are correct as they are. Any future need for a consistent view is served by holding the session monitor across the calls, or by a snapshot API - never by synchronizing these readers.

  • Constructor Details

  • Method Details

    • addListener

      public void addListener(HostSessionListener listener)
      Adds a listener for host session changes.
      Parameters:
      listener - The host session listener.
    • removeListener

      public void removeListener(HostSessionListener listener)
      Removes a listener for host session changes.
      Parameters:
      listener - The host session listener.
    • getListeners

      public HostSessionListener[] getListeners()
      Gets the listeners.
    • getProperties

      public TerminalProps getProperties()
      Gets the terminal properties.
    • getKeyboardRemapper

      public KeyboardRemapper getKeyboardRemapper()
      Gets the keyboard remapper for this session.
    • getType

      public TerminalProps.Type getType()
      Gets the terminal type.
    • getStringType

      public String getStringType()
      Gets the type of session as a String (3270, 5250, EE [when not connected], EE-3270 or EE-5250 [when EE is connected]).
    • getID

      public String getID()
      Gets the terminal session ID.
    • is3270

      public boolean is3270()
      Checks the session type against 3270. If no session type is available, 3270 is assumed.
    • isConnected

      public boolean isConnected()
      Checks for session currently connected.
    • isDisconnected

      public boolean isDisconnected()
      Checks if session is disconnected.
    • isConnecting

      public boolean isConnecting()
      Flag for session is processing a connect request.
    • isReconnecting

      public boolean isReconnecting()
      Check if the session is current being reconnected (e.g. to avoid logging of host sessions being established, disconnected).
    • isReconnecting

      public boolean isReconnecting(HostSessionPeer peer)
      Check if the session is current being reconnected (e.g. to avoid logging of host sessions being established, disconnected).
    • getLastException

      public Throwable getLastException()
      Gets the last communication exception, reset at connect.
      Returns:
      The exception, or null for none.
    • connect

      public boolean connect()
      Connects the session.

      Synchronized on the session monitor: makes the isConnected/isConnecting check-then-act atomic (two racing connects - reconnect thread vs. user action - could otherwise both pass the check and create two TelnetComm instances), and puts the onStateChange calls (whose type-ahead replay writes to the peer) under the same monitor as every other writer. Holding the monitor across peer.connect() is not new - the peers take synchronized(owner) themselves - and does not block: SocketCommNIO.open() initiates a NON-blocking NIO connect and returns (completion arrives via the worker's OP_CONNECT), and Worker.register() is measured non-blocking. The one potentially slow step, DNS resolution inside open(), already ran under this monitor before this change.

    • disconnect

      public boolean disconnect()
      Disconnects the session.

      Synchronized on the session monitor. NOT redundant with the peers' own synchronized(owner) in peer.disconnect(): that hold starts at the peer call, while the isSuspended/firstReconnect writes, the onStateChange (type-ahead replay) on the not-connected branch, clearTypeAhead() and the isConnected check-then-act before the peer call were lock-free. This makes the whole method one unit on the same monitor.

    • sendString

      public boolean sendString(String keys)
      Sends character keystrokes to host.
    • sendString

      public boolean sendString(String keys, boolean allowTypeAhead)
      Sends character keystrokes to host.

      Synchronized on the session monitor for the WHOLE string, not per piece: the pieces go through sendCharacterString(String) / sendKey(int) (reentrant), and holding the monitor across the loop makes an "@"-string atomic against the host thread - no host record can be processed between two of its pieces.

    • sendCharacterString

      public boolean sendCharacterString(String keys)
      Sends character keystrokes to host.
    • sendCharacterString

      public boolean sendCharacterString(String keys, boolean allowTypeAhead)
      Sends character keystrokes to host without allowing type-ahead.

      Synchronized on the session monitor: the type-ahead check-then-act (queue non-empty or keyboard locked -> queue, else write) must be atomic with the peer write that follows, or the host thread can unlock and replay the queue between the two.

    • isTypeAheadEnabled

      public boolean isTypeAheadEnabled()
      Checks if type-ahead is enabled for this session.
    • sendKey

      public boolean sendKey(int key)
      Sends keystrokes to host without allowing type-ahead.
      See Also:
    • sendKey

      public boolean sendKey(int key, boolean allowTypeAhead)
      Sends keystrokes to host.

      Synchronized on the session monitor, same reason as sendCharacterString(String).

      See Also:
    • printSession

      public boolean printSession(HostSessionPeer peer)
      Print terminal session.
      Returns:
      false if this is not a Java client session, true otherwise.
    • doesHostNeedFieldExit

      public boolean doesHostNeedFieldExit(int x, int y, int offset, int length, String string)
      For 5250 only: checks if a field has the attributes that requires this particular string to enter the data followed by a "Field Exit" or "Field Minus" key. For 3270 it's always false.

      Synchronized on the session monitor although it only reads: it looks up the 5250 field table the host thread rebuilds per record, and callers pair it with a following setHostStringAsKeys(int, int, int, int, String)/setHostString(int, int, int, int, String). Each call is consistent on its own; the pair is not atomic across the two calls.

    • setHostString

      public boolean setHostString(int x, int y, int offset, int length, String string)
      Sets a string to a host field.

      Synchronized on the session monitor for the whole method, not just the peer write: the minimal-diff computation below reads the screen mirror first, and that read must see the same screen the write then applies to.

    • setHostStringAsKeys

      public boolean setHostStringAsKeys(int x, int y, int offset, int length, String string)
      Sets a string to a host field just as if the user typed the characters. The host field is first cleared with (3270) Clear EOF or (5250) Field Exit or Field Minus (for negative numbers in a numeric field). The string is then entered as a set of keystrokes, followed by a potential Field Exit or Field Minus (for negative numbers in a numeric field), but only for 5250.
    • setCursor

      public boolean setCursor(int x, int y)
      Sets the host cursor position.
    • getCursor

      public Position getCursor()
      Gets the current host cursor position.
    • getCharactersAndAttributes

      public void getCharactersAndAttributes(char[] chars, int[] attrbs, int beginPos, int endPos)
      Gets the characters and attributes between two positions, including the end position.

      Synchronized on the session monitor: a bulk read of the live display buffers, so the range is one consistent snapshot rather than a mix of two host records. Reentrant from onScreenChange(HostSessionPeer, int, int) (the mirror update), which already runs under the monitor.

    • getCharactersAndAttributes

      public void getCharactersAndAttributes(char[] chars, int[] attrbs, int beginPos, int endPos, char fieldOrNullChar)
      Gets the characters and attributes between two positions, including the end position. The character "fieldOrNullChar" is the character that will be used instead of field attributes and null characters.
    • snapshot

      public HostScreenSnapshot snapshot()
      Takes a consistent, immutable snapshot of the screen MIRROR: characters, attributes, size, cursor position, field model and the screen generation, copied in one hold of the session monitor. The returned object is the caller's own - its arrays are copies, never aliases of live state - and it silently becomes STALE as the session moves on, never invalid and never torn.

      This is MCP/out-of-process infrastructure: it exists for callers that cannot hold the session monitor across their reads (a monitor cannot be held across a protocol round-trip). An in-process caller that can hold the monitor should do that instead - synchronized(hostSession) around its reads - rather than snapshot: cheaper, and some in-process readers (the EEScreen capture) need the PEER's raw buffers, which differ from the mirror's '\0'-substituted content.

      CONSISTENT IS NOT SETTLED. Two separate facts: (1) a snapshot can never be taken mid-record, because the worker holds this monitor for the whole record - every snapshot is record-boundary consistent. (2) A record boundary is NOT a finished screen: hosts paint a screen in SEVERAL records (write, write, unlock keyboard), so a snapshot between records is consistent but may be premature - an agent acting on it acts on a half-painted screen, which this method cannot detect. Until a settled-screen API exists, the poll-side approximation available today is: getScreenGeneration() unchanged for a quiet period AND isLocked() false (keyboard unlocked).

      Returns:
      The snapshot, never null.
    • getScreenGeneration

      public long getScreenGeneration()
      Gets the screen generation: a counter incremented on every mirror mutation (screen data, size, cursor, field model). Two equal values mean nothing a snapshot captures changed between the two reads. Volatile read, no lock - poll this cheaply from any thread and call snapshot() only on change.
    • onScreenChange

      public void onScreenChange(HostSessionPeer peer, int beginPos, int endPos)
      Notifies the session of a screen change.
    • onScreenChange

      public void onScreenChange(RemoteHostSessionListener listener, HostSessionPeer peer, int beginPos, int endPos)
      Method to update the screen of a RemoteHostScreenListener.
      Parameters:
      listener - The listener to update, or null for all.
      peer - The peer.
    • getScreenCompression

      public ScreenCompression getScreenCompression()
      Gets the screen compressor.
    • onFieldChange

      public void onFieldChange(HostSessionPeer peer)
      Notifies the session that fields have changed.
    • onScreenSizeChange

      public void onScreenSizeChange(HostSessionPeer peer, int cx, int cy)
      Notifies the session that the screen size has changed.
    • onCursorPositionChange

      public void onCursorPositionChange(HostSessionPeer peer, int x, int y)
      Notifies the session that the cursor position has changed.
    • onConnectChange

      public void onConnectChange(HostSessionPeer peer, boolean connected)
      Notifies the session of a connect state change.
    • onStateChange

      public void onStateChange(HostSessionPeer peer)
      Notifies the session of a state change (such as insert mode, lock state, error state).
    • getScreen

      public HostScreen getScreen()
      Gets the current host screen instance.
    • getHostScreenSize

      public Size getHostScreenSize()
      Gets the host screen size.
    • getHostScreenLength

      public int getHostScreenLength()
      Gets the current screen length (width * height).
    • getSessionState

      public int getSessionState()
      Gets the host session state.
      Returns:
      A combination of STATE_* flags is returned.
    • isLocked

      public boolean isLocked()
      Checks if the host session is locked.
    • isKeyboardInputAllowed

      public boolean isKeyboardInputAllowed()
      Checks if the host session is locked for keyboard input. Keyboard input is allowed for 5250 sessions when in System Request mode or the Attention key is sent.
    • hasError

      public boolean hasError()
      Checks if there is an error state.
    • inInsertMode

      public boolean inInsertMode()
      Checks if the keyboard is in insert mode or not.
    • refreshHostFields

      public final void refreshHostFields()
      Refreshes the host fields of the current session. The host session will add all available host fields sorted left to right, top to bottom.

      Synchronized on the session monitor for the whole rebuild: initialize() sizes the field model from the screen mirror and the peer then fills it by scanning its own display buffer - the two must be the same screen, and the readers of the field model must not see it half rebuilt. All current callers already hold the monitor (they run inside host event callbacks); this makes it true for any caller.

    • getSessionInfo

      public SessionInfo getSessionInfo()
      Gets the current SessionInfo.
    • soundAlarm

      public void soundAlarm(HostSessionPeer peer)
      Sound alarm on the client.
    • onSessionFailure

      public void onSessionFailure(HostSessionPeer peer, Throwable exception)
      Called when a session has had a failure.
    • getLastError

      public String getLastError(boolean doClear)
      Gets the last error message of the session.
      Returns:
      null if the session doesn't support it (3270 doesn't).
    • setLastError

      public void setLastError(String errMsg)
      Sets the last error message of the session.
    • getDeviceName

      public String getDeviceName()
      Gets the device name of the session.
      Returns:
      null if no device name exists.
    • onHostPrintPage

      public void onHostPrintPage(HostSessionPeer peer)
      Called when a host printer needs printing of a new page.
    • getPeer

      public HostSessionPeer getPeer()
      Gets the peer.

      Note that manipulation of the peer directly needs synchronization of that object.

    • clearTypeAhead

      public void clearTypeAhead()
      Clears the type-ahead buffer.

      Synchronized on the session monitor: typeAhead is replaced here and read/consumed by sendKey(int), sendCharacterString(String) and the replay in onStateChange(HostSessionPeer) (which runs under the same monitor when host-driven). Reentrant from those.

    • setInsertMode

      public boolean setInsertMode(boolean on)
      Sets the insert mode.
      Returns:
      true for success, false for failure.
    • replaceOwner

      public void replaceOwner(HostSessionOwner owner)
      Replaces the host session owner for this session.
    • isProcessingDataStream

      public boolean isProcessingDataStream()
      Checks if the session is currently processing data stream commands to update the screen/cursor, state, etc.
    • onHostDataStreamProcessing

      public void onHostDataStreamProcessing(HostSessionPeer peer, boolean isEntering)
      Called when entering or exiting data stream processing.
    • getAllowedCharacters

      public String getAllowedCharacters()
      Gets the string of characters that can be input by the user using the configured host code page for this session.

      If the return string is empty, for the EE package for example, all characters are "inputable".

    • isFieldFormatted

      public boolean isFieldFormatted()
      Checks if this host screen is field formatted. This method is not like the one in HostScreen, this one checks directly to see if any fields are present.
    • getWorker

      public Worker getWorker() throws IOException
      Gets the worker instance of the session.
      Throws:
      IOException
    • performCopy

      public String performCopy(int oper, int x, int y, int cx, int cy)
      Performs a copy operation to a clip board string for terminal screen.
      Parameters:
      oper - 0=divide by field, 1=by word, 2=data only.
      x - The x position.
      y - The y position.
      cx - The width.
      cy - The height.
      Returns:
      null if the operation could not be completed due to invalid host session or parameters, otherwise the copied string.

      Synchronized on the session monitor: a compound read of the screen mirror and its field model, so the copied rectangle is one screen. Reads only; the caller puts the returned string on the clipboard AFTER this returns - no UI resource is touched under the monitor.

    • performClear

      public boolean performClear(int x, int y, int cx, int cy)
      Performs a clear operation in the terminal screen.
      Returns:
      false if the operation could not be completed due to invalid host session or parameters, true otherwise.

      Synchronized on the session monitor for the whole operation: the field walk (which fields overlap the rectangle, which are editable) and the per-field setHostString(int, int, int, int, String) writes it decides from that walk are one unit against the host thread. The writes fire onScreenChange under the monitor - already the case per call, now for the whole rectangle.

    • performPaste

      public String performPaste(String data, boolean byField, boolean doPasteTextWrapping)
      Performs a paste operation.
      Parameters:
      byField - Flag used only when tabs are in the string.
      doPasteTextWrapping - Flag to paste too long text wrapping.
      Returns:
      The rest of the clip board data that couldn't be pasted (in order to perform another Continue paste operation), or null for failure.

      Synchronized on the session monitor for the whole operation: it reads the cursor and the field model to decide where each piece goes, then writes via setHostString(int, int, int, int, String) - the decision and the writes are one unit against the host thread. data is caller input (the Designer reads the clipboard and asks its questions BEFORE calling); nothing here touches a UI resource under the monitor.

    • performSuspend

      public boolean performSuspend()
      Suspends or resumes the session.
      Returns:
      true for success, false for failure.
    • isSuspended

      public boolean isSuspended()
      Checks if session is suspended.
    • performResume

      public boolean performResume()
      Resumes the session.
      Returns:
      true for success, false for failure.
    • canConnect

      public boolean canConnect()
      Checks for connect operation possible, used typically for GUI operations.
    • canSuspend

      public boolean canSuspend()
      Checks for suspend operation possible, used typically for GUI operations.
    • canResume

      public boolean canResume()
      Checks for resume operation possible, used typically for GUI operations.
    • canDisconnect

      public boolean canDisconnect()
      Checks for disconnect operation possible, used typically for GUI operations.
    • isCaptured

      public boolean isCaptured()
      Checks if this is a screen capture or a "real" 3270/5250 session.
    • getInfo

      public String getInfo()
      Gets the info of the Terminal Session. The name is either the file name for a capture session or the terminal properties name. The first character is '0'=live 3270/5250, '1'=capture, '2'=recorded.