view src/org/tn5250j/framework/tn5250/DataStreamProducer.java @ 98:16e023784917

protect both read calls with test for blocking
author Carl Byington <carl@five-ten-sg.com>
date Tue, 17 Jun 2014 11:35:01 -0700
parents 8181cb01c64d
children eda03b809f48
line wrap: on
line source

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 org.tn5250j.encoding.ICodePage;

import android.util.Log;

import de.mud.terminal.vt320;


public class DataStreamProducer implements Runnable {
    private static final String TAG = "DataStreamProducer";
   private tnvt                vt;
   private BufferedInputStream bin;
   private vt320               buffer;
   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, vt320 buffer, BlockingQueue<Object> queue, byte[] init) {
      this.bin    = bin;
      this.vt     = vt;
      this.buffer = buffer;
      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;

      while(!done) {
         if (bin.available() == 0) buffer.testChanged();
         i = bin.read();

         // 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) {
            // get the negotiation option
            if (bin.available() == 0) buffer.testChanged();
            baosin.write(bin.read());
            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)));
//      }

}