diff app/src/main/java/org/tn5250j/framework/tn5250/DataStreamProducer.java @ 438:d29cce60f393

migrate from Eclipse to Android Studio
author Carl Byington <carl@five-ten-sg.com>
date Thu, 03 Dec 2015 11:23:55 -0800
parents src/org/tn5250j/framework/tn5250/DataStreamProducer.java@24186858750f
children 8fa8e73e2f5c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/src/main/java/org/tn5250j/framework/tn5250/DataStreamProducer.java	Thu Dec 03 11:23:55 2015 -0800
@@ -0,0 +1,372 @@
+package org.tn5250j.framework.tn5250;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.SocketException;
+import java.util.concurrent.BlockingQueue;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.tn5250j.encoding.ICodePage;
+
+import android.util.Log;
+
+
+public class DataStreamProducer implements Runnable {
+    private static final String TAG = "DataStreamProducer";
+    private tnvt                vt;
+    private BufferedInputStream bin;
+    private ByteArrayOutputStream baosin;
+    private Thread me;
+    private byte[] saveStream;
+    private final BlockingQueue<Object> dsq;
+    private byte[] abyte2;
+    private FileOutputStream fw;
+    private BufferedOutputStream dw;
+    private boolean dumpBytes = false;
+    private ICodePage codePage;
+
+
+
+    public DataStreamProducer(tnvt vt, BufferedInputStream bin, BlockingQueue<Object> queue, byte[] init) {
+        this.bin    = bin;
+        this.vt     = vt;
+        baosin = new ByteArrayOutputStream();
+        dsq = queue;
+        abyte2 = init;
+    }
+
+    public void setInputStream(ByteArrayOutputStream is) {
+        baosin = is;
+    }
+
+    public final void run() {
+        boolean done = false;
+        me = Thread.currentThread();
+        // load the first response screen
+        loadStream(abyte2, 0);
+
+        while (!done) {
+            try {
+                byte[] abyte0 = readIncoming();
+
+                // WVL - LDC : 17/05/2004 : Device name negotiations send TIMING MARK
+                // Restructured to the readIncoming() method to return null
+                // on TIMING MARK. Don't process in that case (abyte0 == null)!
+                if (abyte0 != null) {
+                    // WVL - LDC : 16/07/2003 : TR.000345
+                    // When the socket has been closed, the reading returns
+                    // no bytes (an empty byte arrray).
+                    // But the loadStream fails on this, so we check it here!
+                    if (abyte0.length > 0) {
+                        loadStream(abyte0, 0);
+                    }
+                    // WVL - LDC : 16/07/2003 : TR.000345
+                    // Returning no bytes means the input buffer has
+                    // reached end-of-stream, so we do a disconnect!
+                    else {
+                        done = true;
+                        vt.disconnect();
+                    }
+                }
+            }
+            catch (SocketException se) {
+                Log.w(TAG, "   DataStreamProducer thread interrupted and stopping " + se.getMessage());
+                done = true;
+            }
+            catch (IOException ioe) {
+                Log.w(TAG, ioe.getMessage());
+
+                if (me.isInterrupted())
+                    done = true;
+            }
+            catch (Exception ex) {
+                Log.w(TAG, ex.getMessage());
+
+                if (me.isInterrupted())
+                    done = true;
+            }
+        }
+    }
+
+    private final void loadStream(byte abyte0[], int i) {
+        int j = 0;
+        int size = 0;
+
+        if (saveStream == null) {
+            j = (abyte0[i] & 0xff) << 8 | abyte0[i + 1] & 0xff;
+            size = abyte0.length;
+        }
+        else {
+            size = saveStream.length + abyte0.length;
+            byte[] inter = new byte[size];
+            System.arraycopy(saveStream, 0, inter, 0, saveStream.length);
+            System.arraycopy(abyte0, 0, inter, saveStream.length, abyte0.length);
+            abyte0 = new byte[size];
+            System.arraycopy(inter, 0, abyte0, 0, size);
+            saveStream = null;
+            inter = null;
+            j = (abyte0[i] & 0xff) << 8 | abyte0[i + 1] & 0xff;
+            Log.d(TAG, "partial stream found");
+        }
+
+        if (j > size) {
+            saveStream = new byte[abyte0.length];
+            System.arraycopy(abyte0, 0, saveStream, 0, abyte0.length);
+            Log.d(TAG, "partial stream saved");
+        }
+        else {
+            byte abyte1[];
+
+            try {
+                abyte1 = new byte[j + 2];
+                System.arraycopy(abyte0, i, abyte1, 0, j + 2);
+                dsq.put(abyte1);
+
+                if (abyte0.length > abyte1.length + i)
+                    loadStream(abyte0, i + j + 2);
+            }
+            catch (Exception ex) {
+                Log.w(TAG, "load stream error " + ex.getMessage());
+                //        ex.printStackTrace();
+                //        dump(abyte0);
+            }
+        }
+    }
+
+    public final byte[] readIncoming()
+    throws IOException {
+        boolean done = false;
+        boolean negotiate = false;
+        baosin.reset();
+        int j = -1;
+        int i = 0;
+        Timer timer = new Timer("data.stream", true);
+        TimerTask task = null;
+
+        while (!done) {
+            if (bin.available() == 0) {
+                task = new TimerTask() {
+                    public void run() {
+                        try {
+                            dsq.put(new Integer(0));  // trigger buffer.testChanged()
+                        }
+                        catch (Exception ex) {
+                            Log.w(TAG, "readIncoming error " + ex.getMessage());
+                        }
+                    }
+                };
+                timer.schedule(task, 10);   // 10 ms delay
+            }
+
+            i = bin.read();
+
+            if (task != null) {
+                task.cancel();
+                task = null;
+            }
+
+            // WVL - LDC : 16/07/2003 : TR.000345
+            // The inStream return -1 when end-of-stream is reached. This
+            // happens e.g. when the connection is closed from the AS/400.
+            // So we stop in this case!
+            // ==> an empty byte array is returned from this method.
+            if (i == -1) { // nothing read!
+                done = true;
+                vt.disconnect();
+                continue;
+            }
+
+            // We use the values instead of the static values IAC and EOR
+            //    because they are defined as bytes.
+            //
+            // The > if(i != 255 || j != 255)  < is a hack for the double FF FF's
+            // that are being returned.  I do not know why this is like this and
+            // can not find any documentation for it.  It is also being returned
+            // on my Client Access tcp dump as well so they are handling it.
+            //
+            // my5250
+            // 0000:  00 50 DA 44 C8 45 42 00 00 00 00 24 08 00 45 00 .P.D.EB....$..E.
+            // 0010:  04 2A BC F9 00 00 40 06 D0 27 C1 A8 33 04 C1 A8 .*....@..'..3...
+            // 0020:  33 58 00 17 04 18 6F A2 83 CB 00 1E D1 BA 50 18 3X....o.......P.
+            // 0030:  20 00 8A 9A 00 00 03 FF FF 12 A0 00 00 04 00 00  ...............
+            // --------------------------- || || -------------------------------------
+            // 0040:  03 04 40 04 11 00 20 01 07 00 00 00 18 00 00 10 ..@... .........
+
+            if (j == 255 && i == 255) {
+                j = -1;
+                continue;
+            }
+
+            baosin.write(i);
+
+            // check for end of record EOR and IAC  - FFEF
+            if (j == 255 && i == 239)
+                done = true;
+
+            // This is to check for the TELNET TIMING MARK OPTION
+            // rfc860 explains this in more detail.  When we receive it
+            // we will negotiate with the server by sending a WONT'T TIMING-MARK
+            // This will let the server know that we processed the information
+            // and are just waiting for the user to enter some data so keep the
+            // socket alive.   This is more or less a AYT (ARE YOU THERE) or not.
+            if (i == 253 && j == 255) {
+                done = true;
+                negotiate = true;
+            }
+
+            j = i;
+        }
+
+        // after the initial negotiation we might get other options such as
+        //    timing marks ??????????????  do we ???????????? look at telnet spec
+        // yes we do. rfc860 explains about timing marks.
+        // WVL - LDC : 17/05/2004 : Device name negotiations send TIMING MARK
+        //                          to existing device!
+        // Handled incorrectly: we cannot continue processing the TIMING MARK DO
+        // after we have handled it in the vt.negotiate()
+        // We should not return the bytes;
+        // ==> restructured to return null after negotiation!
+        //     Impacts the run method! Added the null check.
+        byte[] rBytes = baosin.toByteArray();
+
+        if (dumpBytes) {
+            dump(rBytes);
+        }
+
+        if (negotiate) {
+            if (bin.available() == 0) {
+                task = new TimerTask() {
+                    public void run() {
+                        try {
+                            dsq.put(new Integer(0));  // trigger buffer.testChanged()
+                        }
+                        catch (Exception ex) {
+                            Log.w(TAG, "readIncoming error " + ex.getMessage());
+                        }
+                    }
+                };
+                timer.schedule(task, 10);   // 10 ms delay
+            }
+
+            // get the negotiation option
+            baosin.write(bin.read());
+
+            if (task != null) {
+                task.cancel();
+                task = null;
+            }
+
+            vt.negotiate(rBytes);
+            return null;
+        }
+
+        return rBytes;
+    }
+
+    protected final void toggleDebug(ICodePage cp) {
+        if (codePage == null)
+            codePage = cp;
+
+        dumpBytes = !dumpBytes;
+
+        if (dumpBytes) {
+            try {
+                if (fw == null) {
+                    fw = new FileOutputStream("log.txt");
+                    dw = new BufferedOutputStream(fw);
+                }
+            }
+            catch (FileNotFoundException fnfe) {
+                Log.w(TAG, fnfe.getMessage());
+            }
+        }
+        else {
+            try {
+                if (dw != null)
+                    dw.close();
+
+                if (fw != null)
+                    fw.close();
+
+                dw = null;
+                fw = null;
+                codePage = null;
+            }
+            catch (IOException ioe) {
+                Log.w(TAG, ioe.getMessage());
+            }
+        }
+
+        Log.i(TAG, "Data Stream output is now " + dumpBytes);
+    }
+
+    public void dump(byte[] abyte0) {
+        try {
+            Log.i(TAG, "\n Buffer Dump of data from AS400: ");
+            dw.write("\r\n Buffer Dump of data from AS400: ".getBytes());
+            StringBuffer h = new StringBuffer();
+
+            for (int x = 0; x < abyte0.length; x++) {
+                if (x % 16 == 0) {
+                    System.out.println("  " + h.toString());
+                    dw.write(("  " + h.toString() + "\r\n").getBytes());
+                    h.setLength(0);
+                    h.append("+0000");
+                    h.setLength(5 - Integer.toHexString(x).length());
+                    h.append(Integer.toHexString(x).toUpperCase());
+                    System.out.print(h.toString());
+                    dw.write(h.toString().getBytes());
+                    h.setLength(0);
+                }
+
+                char ac = codePage.ebcdic2uni(abyte0[x]);
+
+                if (ac < ' ')
+                    h.append('.');
+                else
+                    h.append(ac);
+
+                if (x % 4 == 0) {
+                    System.out.print(" ");
+                    dw.write((" ").getBytes());
+                }
+
+                if (Integer.toHexString(abyte0[x] & 0xff).length() == 1) {
+                    System.out.print("0" + Integer.toHexString(abyte0[x] & 0xff).toUpperCase());
+                    dw.write(("0" + Integer.toHexString(abyte0[x] & 0xff).toUpperCase()).getBytes());
+                }
+                else {
+                    System.out.print(Integer.toHexString(abyte0[x] & 0xff).toUpperCase());
+                    dw.write((Integer.toHexString(abyte0[x] & 0xff).toUpperCase()).getBytes());
+                }
+            }
+
+            System.out.println();
+            dw.write("\r\n".getBytes());
+            dw.flush();
+        }
+        catch (EOFException _ex) { }
+        catch (Exception _ex) {
+            Log.w(TAG, "Cannot dump from host\n\r");
+        }
+    }
+
+//      public void dumpBytes() {
+//         byte shit[] = bk.buffer;
+//         for (int i = 0;i < shit.length;i++)
+//            System.out.println(i + ">" + shit[i] + "< - ascii - >" + getASCIIChar(shit[i]) + "<");
+//      }
+//
+//      public void dumpHexBytes(byte[] abyte) {
+//         byte shit[] = abyte;
+//         for (int i = 0;i < shit.length;i++)
+//            System.out.println(i + ">" + shit[i] + "< hex >" + Integer.toHexString((shit[i] & 0xff)));
+//      }
+
+}