Mercurial > 510Connectbot
comparison 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 |
comparison
equal
deleted
inserted
replaced
437:208b31032318 | 438:d29cce60f393 |
---|---|
1 package org.tn5250j.framework.tn5250; | |
2 | |
3 import java.io.BufferedInputStream; | |
4 import java.io.BufferedOutputStream; | |
5 import java.io.ByteArrayOutputStream; | |
6 import java.io.EOFException; | |
7 import java.io.FileNotFoundException; | |
8 import java.io.FileOutputStream; | |
9 import java.io.IOException; | |
10 import java.net.SocketException; | |
11 import java.util.concurrent.BlockingQueue; | |
12 import java.util.Timer; | |
13 import java.util.TimerTask; | |
14 | |
15 import org.tn5250j.encoding.ICodePage; | |
16 | |
17 import android.util.Log; | |
18 | |
19 | |
20 public class DataStreamProducer implements Runnable { | |
21 private static final String TAG = "DataStreamProducer"; | |
22 private tnvt vt; | |
23 private BufferedInputStream bin; | |
24 private ByteArrayOutputStream baosin; | |
25 private Thread me; | |
26 private byte[] saveStream; | |
27 private final BlockingQueue<Object> dsq; | |
28 private byte[] abyte2; | |
29 private FileOutputStream fw; | |
30 private BufferedOutputStream dw; | |
31 private boolean dumpBytes = false; | |
32 private ICodePage codePage; | |
33 | |
34 | |
35 | |
36 public DataStreamProducer(tnvt vt, BufferedInputStream bin, BlockingQueue<Object> queue, byte[] init) { | |
37 this.bin = bin; | |
38 this.vt = vt; | |
39 baosin = new ByteArrayOutputStream(); | |
40 dsq = queue; | |
41 abyte2 = init; | |
42 } | |
43 | |
44 public void setInputStream(ByteArrayOutputStream is) { | |
45 baosin = is; | |
46 } | |
47 | |
48 public final void run() { | |
49 boolean done = false; | |
50 me = Thread.currentThread(); | |
51 // load the first response screen | |
52 loadStream(abyte2, 0); | |
53 | |
54 while (!done) { | |
55 try { | |
56 byte[] abyte0 = readIncoming(); | |
57 | |
58 // WVL - LDC : 17/05/2004 : Device name negotiations send TIMING MARK | |
59 // Restructured to the readIncoming() method to return null | |
60 // on TIMING MARK. Don't process in that case (abyte0 == null)! | |
61 if (abyte0 != null) { | |
62 // WVL - LDC : 16/07/2003 : TR.000345 | |
63 // When the socket has been closed, the reading returns | |
64 // no bytes (an empty byte arrray). | |
65 // But the loadStream fails on this, so we check it here! | |
66 if (abyte0.length > 0) { | |
67 loadStream(abyte0, 0); | |
68 } | |
69 // WVL - LDC : 16/07/2003 : TR.000345 | |
70 // Returning no bytes means the input buffer has | |
71 // reached end-of-stream, so we do a disconnect! | |
72 else { | |
73 done = true; | |
74 vt.disconnect(); | |
75 } | |
76 } | |
77 } | |
78 catch (SocketException se) { | |
79 Log.w(TAG, " DataStreamProducer thread interrupted and stopping " + se.getMessage()); | |
80 done = true; | |
81 } | |
82 catch (IOException ioe) { | |
83 Log.w(TAG, ioe.getMessage()); | |
84 | |
85 if (me.isInterrupted()) | |
86 done = true; | |
87 } | |
88 catch (Exception ex) { | |
89 Log.w(TAG, ex.getMessage()); | |
90 | |
91 if (me.isInterrupted()) | |
92 done = true; | |
93 } | |
94 } | |
95 } | |
96 | |
97 private final void loadStream(byte abyte0[], int i) { | |
98 int j = 0; | |
99 int size = 0; | |
100 | |
101 if (saveStream == null) { | |
102 j = (abyte0[i] & 0xff) << 8 | abyte0[i + 1] & 0xff; | |
103 size = abyte0.length; | |
104 } | |
105 else { | |
106 size = saveStream.length + abyte0.length; | |
107 byte[] inter = new byte[size]; | |
108 System.arraycopy(saveStream, 0, inter, 0, saveStream.length); | |
109 System.arraycopy(abyte0, 0, inter, saveStream.length, abyte0.length); | |
110 abyte0 = new byte[size]; | |
111 System.arraycopy(inter, 0, abyte0, 0, size); | |
112 saveStream = null; | |
113 inter = null; | |
114 j = (abyte0[i] & 0xff) << 8 | abyte0[i + 1] & 0xff; | |
115 Log.d(TAG, "partial stream found"); | |
116 } | |
117 | |
118 if (j > size) { | |
119 saveStream = new byte[abyte0.length]; | |
120 System.arraycopy(abyte0, 0, saveStream, 0, abyte0.length); | |
121 Log.d(TAG, "partial stream saved"); | |
122 } | |
123 else { | |
124 byte abyte1[]; | |
125 | |
126 try { | |
127 abyte1 = new byte[j + 2]; | |
128 System.arraycopy(abyte0, i, abyte1, 0, j + 2); | |
129 dsq.put(abyte1); | |
130 | |
131 if (abyte0.length > abyte1.length + i) | |
132 loadStream(abyte0, i + j + 2); | |
133 } | |
134 catch (Exception ex) { | |
135 Log.w(TAG, "load stream error " + ex.getMessage()); | |
136 // ex.printStackTrace(); | |
137 // dump(abyte0); | |
138 } | |
139 } | |
140 } | |
141 | |
142 public final byte[] readIncoming() | |
143 throws IOException { | |
144 boolean done = false; | |
145 boolean negotiate = false; | |
146 baosin.reset(); | |
147 int j = -1; | |
148 int i = 0; | |
149 Timer timer = new Timer("data.stream", true); | |
150 TimerTask task = null; | |
151 | |
152 while (!done) { | |
153 if (bin.available() == 0) { | |
154 task = new TimerTask() { | |
155 public void run() { | |
156 try { | |
157 dsq.put(new Integer(0)); // trigger buffer.testChanged() | |
158 } | |
159 catch (Exception ex) { | |
160 Log.w(TAG, "readIncoming error " + ex.getMessage()); | |
161 } | |
162 } | |
163 }; | |
164 timer.schedule(task, 10); // 10 ms delay | |
165 } | |
166 | |
167 i = bin.read(); | |
168 | |
169 if (task != null) { | |
170 task.cancel(); | |
171 task = null; | |
172 } | |
173 | |
174 // WVL - LDC : 16/07/2003 : TR.000345 | |
175 // The inStream return -1 when end-of-stream is reached. This | |
176 // happens e.g. when the connection is closed from the AS/400. | |
177 // So we stop in this case! | |
178 // ==> an empty byte array is returned from this method. | |
179 if (i == -1) { // nothing read! | |
180 done = true; | |
181 vt.disconnect(); | |
182 continue; | |
183 } | |
184 | |
185 // We use the values instead of the static values IAC and EOR | |
186 // because they are defined as bytes. | |
187 // | |
188 // The > if(i != 255 || j != 255) < is a hack for the double FF FF's | |
189 // that are being returned. I do not know why this is like this and | |
190 // can not find any documentation for it. It is also being returned | |
191 // on my Client Access tcp dump as well so they are handling it. | |
192 // | |
193 // my5250 | |
194 // 0000: 00 50 DA 44 C8 45 42 00 00 00 00 24 08 00 45 00 .P.D.EB....$..E. | |
195 // 0010: 04 2A BC F9 00 00 40 06 D0 27 C1 A8 33 04 C1 A8 .*....@..'..3... | |
196 // 0020: 33 58 00 17 04 18 6F A2 83 CB 00 1E D1 BA 50 18 3X....o.......P. | |
197 // 0030: 20 00 8A 9A 00 00 03 FF FF 12 A0 00 00 04 00 00 ............... | |
198 // --------------------------- || || ------------------------------------- | |
199 // 0040: 03 04 40 04 11 00 20 01 07 00 00 00 18 00 00 10 ..@... ......... | |
200 | |
201 if (j == 255 && i == 255) { | |
202 j = -1; | |
203 continue; | |
204 } | |
205 | |
206 baosin.write(i); | |
207 | |
208 // check for end of record EOR and IAC - FFEF | |
209 if (j == 255 && i == 239) | |
210 done = true; | |
211 | |
212 // This is to check for the TELNET TIMING MARK OPTION | |
213 // rfc860 explains this in more detail. When we receive it | |
214 // we will negotiate with the server by sending a WONT'T TIMING-MARK | |
215 // This will let the server know that we processed the information | |
216 // and are just waiting for the user to enter some data so keep the | |
217 // socket alive. This is more or less a AYT (ARE YOU THERE) or not. | |
218 if (i == 253 && j == 255) { | |
219 done = true; | |
220 negotiate = true; | |
221 } | |
222 | |
223 j = i; | |
224 } | |
225 | |
226 // after the initial negotiation we might get other options such as | |
227 // timing marks ?????????????? do we ???????????? look at telnet spec | |
228 // yes we do. rfc860 explains about timing marks. | |
229 // WVL - LDC : 17/05/2004 : Device name negotiations send TIMING MARK | |
230 // to existing device! | |
231 // Handled incorrectly: we cannot continue processing the TIMING MARK DO | |
232 // after we have handled it in the vt.negotiate() | |
233 // We should not return the bytes; | |
234 // ==> restructured to return null after negotiation! | |
235 // Impacts the run method! Added the null check. | |
236 byte[] rBytes = baosin.toByteArray(); | |
237 | |
238 if (dumpBytes) { | |
239 dump(rBytes); | |
240 } | |
241 | |
242 if (negotiate) { | |
243 if (bin.available() == 0) { | |
244 task = new TimerTask() { | |
245 public void run() { | |
246 try { | |
247 dsq.put(new Integer(0)); // trigger buffer.testChanged() | |
248 } | |
249 catch (Exception ex) { | |
250 Log.w(TAG, "readIncoming error " + ex.getMessage()); | |
251 } | |
252 } | |
253 }; | |
254 timer.schedule(task, 10); // 10 ms delay | |
255 } | |
256 | |
257 // get the negotiation option | |
258 baosin.write(bin.read()); | |
259 | |
260 if (task != null) { | |
261 task.cancel(); | |
262 task = null; | |
263 } | |
264 | |
265 vt.negotiate(rBytes); | |
266 return null; | |
267 } | |
268 | |
269 return rBytes; | |
270 } | |
271 | |
272 protected final void toggleDebug(ICodePage cp) { | |
273 if (codePage == null) | |
274 codePage = cp; | |
275 | |
276 dumpBytes = !dumpBytes; | |
277 | |
278 if (dumpBytes) { | |
279 try { | |
280 if (fw == null) { | |
281 fw = new FileOutputStream("log.txt"); | |
282 dw = new BufferedOutputStream(fw); | |
283 } | |
284 } | |
285 catch (FileNotFoundException fnfe) { | |
286 Log.w(TAG, fnfe.getMessage()); | |
287 } | |
288 } | |
289 else { | |
290 try { | |
291 if (dw != null) | |
292 dw.close(); | |
293 | |
294 if (fw != null) | |
295 fw.close(); | |
296 | |
297 dw = null; | |
298 fw = null; | |
299 codePage = null; | |
300 } | |
301 catch (IOException ioe) { | |
302 Log.w(TAG, ioe.getMessage()); | |
303 } | |
304 } | |
305 | |
306 Log.i(TAG, "Data Stream output is now " + dumpBytes); | |
307 } | |
308 | |
309 public void dump(byte[] abyte0) { | |
310 try { | |
311 Log.i(TAG, "\n Buffer Dump of data from AS400: "); | |
312 dw.write("\r\n Buffer Dump of data from AS400: ".getBytes()); | |
313 StringBuffer h = new StringBuffer(); | |
314 | |
315 for (int x = 0; x < abyte0.length; x++) { | |
316 if (x % 16 == 0) { | |
317 System.out.println(" " + h.toString()); | |
318 dw.write((" " + h.toString() + "\r\n").getBytes()); | |
319 h.setLength(0); | |
320 h.append("+0000"); | |
321 h.setLength(5 - Integer.toHexString(x).length()); | |
322 h.append(Integer.toHexString(x).toUpperCase()); | |
323 System.out.print(h.toString()); | |
324 dw.write(h.toString().getBytes()); | |
325 h.setLength(0); | |
326 } | |
327 | |
328 char ac = codePage.ebcdic2uni(abyte0[x]); | |
329 | |
330 if (ac < ' ') | |
331 h.append('.'); | |
332 else | |
333 h.append(ac); | |
334 | |
335 if (x % 4 == 0) { | |
336 System.out.print(" "); | |
337 dw.write((" ").getBytes()); | |
338 } | |
339 | |
340 if (Integer.toHexString(abyte0[x] & 0xff).length() == 1) { | |
341 System.out.print("0" + Integer.toHexString(abyte0[x] & 0xff).toUpperCase()); | |
342 dw.write(("0" + Integer.toHexString(abyte0[x] & 0xff).toUpperCase()).getBytes()); | |
343 } | |
344 else { | |
345 System.out.print(Integer.toHexString(abyte0[x] & 0xff).toUpperCase()); | |
346 dw.write((Integer.toHexString(abyte0[x] & 0xff).toUpperCase()).getBytes()); | |
347 } | |
348 } | |
349 | |
350 System.out.println(); | |
351 dw.write("\r\n".getBytes()); | |
352 dw.flush(); | |
353 } | |
354 catch (EOFException _ex) { } | |
355 catch (Exception _ex) { | |
356 Log.w(TAG, "Cannot dump from host\n\r"); | |
357 } | |
358 } | |
359 | |
360 // public void dumpBytes() { | |
361 // byte shit[] = bk.buffer; | |
362 // for (int i = 0;i < shit.length;i++) | |
363 // System.out.println(i + ">" + shit[i] + "< - ascii - >" + getASCIIChar(shit[i]) + "<"); | |
364 // } | |
365 // | |
366 // public void dumpHexBytes(byte[] abyte) { | |
367 // byte shit[] = abyte; | |
368 // for (int i = 0;i < shit.length;i++) | |
369 // System.out.println(i + ">" + shit[i] + "< hex >" + Integer.toHexString((shit[i] & 0xff))); | |
370 // } | |
371 | |
372 } |