All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ascert.open.term.i3270.Term3270 Maven / Gradle / Ivy

Go to download

An open source emulator supporting 3270 and potentially later 5250 terminal types.

The newest version!
/*
 * Copyright (c) 2016, 2017 Ascert, LLC.
 * www.ascert.com
 *
 * Based on original code from FreeHost3270, copyright for derivations from original works remain:
 *  Copyright (C) 1998, 2001  Art Gillespie
 *  Copyright (2) 2005 the http://FreeHost3270.Sourceforge.net
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.ascert.open.term.i3270;

import java.util.Scanner;
import java.util.logging.Logger;

import com.ascert.open.ohio.Ohio.OHIO_AID;
import com.ascert.open.ohio.Ohio.OHIO_TYPE;
import com.ascert.open.ohio.OhioPosition;

import com.ascert.open.term.core.AbstractTerminal;
import com.ascert.open.term.core.RWTelnet;
import com.ascert.open.term.core.TermChar;
import com.ascert.open.term.core.TermField;
import com.ascert.open.term.core.TnAction;
import com.ascert.open.term.gui.KeyHandler;

/**
 * This class represents a 3270 session to client implementations, and is the main API for creating 3270 applications.
 *
 * @since 0.1
 */
public class Term3270
    extends AbstractTerminal
{

    private static final Logger log = Logger.getLogger(Term3270.class.getName());

    public static String formatTermTypeString(String baseType, int tnModel, boolean extAttribs)
    {
        return String.format("%s%s-%d%s",
                             !baseType.startsWith("IBM-") ? "IBM-" : "",
                             baseType,
                             tnModel,
                             extAttribs ? "-E" : "");
    }

    private WaitObject waitObject;
    private volatile boolean dataArrived;

    private String baseType;
    private short tnModel;
    private boolean extAttribs;

    // Always false at present. Telnet handler doesn't yet support TN3270E. 
    // Marker purely in place for status line
    private boolean tn3270e = false;

    //private Term3270Char[] chars; // array of current characters
    // Default video attributes
    Term3270Char vaVideoDefault = new Term3270Char(0);

    public Term3270()
    {
        this("IBM-3278-2");
    }

    /**
     * Constructor. Sets the model type of the new 3270 object.
     *
     * 
    *
  • * 2: 24x80 *
  • *
  • * 3: 32x80 *
  • *
  • * 4: 43x80 *
  • *
  • * 5: 27x132 *
  • *
* * * @param modelNumber the 3270 model number. * @param client {@link TnAction} interface used for communicating with the client implementation. */ public Term3270(String termType) { super(); ohTermType = OHIO_TYPE.OHIO_TYPE_3270; this.keyHandler = new KeyHandler3270(this); decodeTermTypeString(termType); // Reconstruct the term type string in case any invalid parts were defaulted this.termType = formatTermTypeString(baseType, tnModel, extAttribs); log.fine("RW3270 termType: " + termType); cols = 80; switch (tnModel) { case 2: rows = 24; break; case 3: rows = 32; break; case 4: rows = 43; break; case 5: rows = 27; cols = 132; break; } initTerm(); // create the 3270 Stream Parser Object. Pass it this // instance so it can call back changes to the field and // character buffer as necessary. tnParser = new Tn3270StreamParser(this); // create the TELNET object setTelnet(new RWTelnet(tnParser)); waitObject = new WaitObject(); } private void decodeTermTypeString(String termType) { Scanner scan = new Scanner(termType).useDelimiter("-"); String ibm = scan.next(); if (!"IBM".equals(ibm)) { log.warning("Unrecognised term manufacturer: " + ibm); } baseType = scan.next(); if (!"3278".equals(baseType) && !"3279".equals(baseType)) { log.warning("Unrecognised term type: " + baseType); baseType = "3278"; } tnModel = (short) scan.nextInt(); if (tnModel < 2 || tnModel > 5) { log.warning("Unrecognised term model: " + tnModel); tnModel = 2; } if (scan.hasNext()) { String ext = scan.next(); if ("E".equals(ext)) { // We don't use the extAttribs part for any validation or handling - purely for term type string creation extAttribs = true; } else { log.warning("Unrecognised term extension: " + ext); } } } public void initTerm() { pages.clear(); initPages(1); setActivePage(0); setDisplayPage(0); //TODO - hack for now to adjust status line 2 rows down. // may replace with a better renderer which statusLine = initTermChars(new Term3270Char[cols], (rows + 1) * cols); } @Override public boolean allowDirectScreenEditing() { return getFields().size() == 0; } @Override public char getEmptyChar() { return 0; } @Override public void Fkey(OHIO_AID key) { super.Fkey(key); if (aid == OHIO_AID.OHIO_AID_3270_CLEAR) { TermChar[] chars = getCharBuffer(); for (int i = 0; i < chars.length; i++) { chars[i].clear(); } resumeParentThread(); } } @Override public TermChar[] getStatusLine() { // Quick hack for now, following x3270 indicators String commStatus = String.format("4%c%c", tn3270e ? 'B' : 'A', tn.isConnected() ? ' ' : '?'); setStatusChars(0, commStatus); // According to various online sources, also marks comm status: // https://msdn.microsoft.com/en-us/library/gg165006(v=bts.70).aspx // https://msdn.microsoft.com/en-us/library/gg165006(v=bts.70).aspx String kbdStatus = " "; if (!tn.isConnected()) { //TODO - need to rewire host_ disconnected status handling so we can detect and show a COMM505 as well kbdStatus = "X COMM504"; } else if (isKeyboardLocked()) { // have no code yet to determine other X statuses e.g X ?+ kbdStatus = "X SYSTEM "; } // Opinions seem to differ on whether it starts in column 9 or 10 setStatusChars(8, kbdStatus); String cursorStatus = String.format("%03d/%03d", getCurrentRow(), getCurrentCol()); setStatusChars(this.getCols() - 7, cursorStatus); log.finer(String.format("comm status: %s (%s)", commStatus, cursorStatus)); return statusLine; } public TermChar getDefaultVAChar() { return vaVideoDefault; } /** * Sets session data on the session server * * @param key DOCUMENT ME! * @param value DOCUMENT ME! */ public void setSessionData(String key, String value) { tn.setSessionData(key, value); } /** * Blocks until the specified string is found in the data buffer (screen) or until the specified timeout is reached. * *

* This method is extrememly useful when the host may be sending several screens in response to your request, and you want to block * until you reach the response you're waiting for. For example:
*

     * ...
     * // Create a Term3270 object
     * Term3270 rw = new Term3270(this);
     * // Connect to the SessionServer and specify a host
     * rw.connect("3.3.88.3", 6870, "hollis.harvard.edu", 23, true);
     * // This will block for 30 seconds or until the string
     * // H A R V A R D is found.  it also returns a boolean to
     * // let us check the results
     * boolean t = rw.waitFor("H A R V A R D", 30);
     * // Now we'll execute two different blocks of code depending
     * // on whether our string was found:
     * if(t) {
     *   out.print("Got it:");
     *   out.print(new String(rw.getDisplay()));
     * } else {
     *   out.print("Didn't get it:>");
     *   out.print(new String(rw.getDisplay()));
     * }
     * 
*

* * @param search the string to wait for. * @param timeout number of seconds to wait for the search string. * * @return true if the string was found, false if the timeout was reached without finding it. */ public boolean waitFor(String search, int timeout) { return waitForText(search, timeout*1000); } public boolean waitForText(String search, long timeoutMillis) { boolean found = contains(search); long cur = System.currentTimeMillis(); long finish = cur + timeoutMillis; while (!found && cur < finish) { waitForNewData(finish-cur); cur = System.currentTimeMillis(); found = contains(search); } return found; } /** * Blocks the currently executing thread until new data arrives from the host. * *

* This method is useful when using stateless client implementations (i.e. HTTP) or when you're scripting through several 3270 screens. *

* *

* For example:
*

     * ...
     * //Set the value of a field and press enter
     * Term3270Field.setData("My Data");
     * Term3270.enter();
     * //Wait for the new screen
     * Term3270.waitForNewData();
     * myClient.paintScreen()....
     * 
*

* *

* IMPORTANT: This method only blocks until the 3270 engine has received a response from the host and processed it. In the case * where the host has sent an EraseAllUnprotected or other Non-Data type command, this method will return normally, even though the * DataBuffer (screen) may be empty. Client implementations need to handle any host-specific anomalies such as screen clears, etc. *

*/ public void waitForNewData() { waitForNewData(0); } public boolean waitForNewData(long timeoutMillis) { synchronized (waitObject) { try { dataArrived = false; waitObject.wait(timeoutMillis); return dataArrived; } catch (InterruptedException e) { } } return false; } protected void resumeParentThread() { if (waitObject != null) { synchronized (waitObject) { dataArrived = true; waitObject.notifyAll(); } } } protected TermChar[] getTermCharPage() { // create the character buffer return initTermChars(new Term3270Char[rows * cols], 0); } @Override protected TermChar[] initTermChars(TermChar[] tch, int baOff) { // create TermChar instances for each position in the databuffer (chars array) for (int i = 0; i < tch.length; i++) { tch[i] = new Term3270Char(i + baOff); } return tch; } protected TermField newTermField(TermChar currChar) { return new Term3270Field((Term3270Char) currChar, this); } static class WaitObject { public synchronized void getLock() { } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy