Source code: com/wilko/jaim/JaimConnection.java
1 /*
2 * (C) 2002 Paul Wilkinson wilko@users.sourceforge.net
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
19
20 /*
21 * JaimConnection.java
22 *
23 * Created on 4 May 2002, 08:38
24 */
25
26 package com.wilko.jaim;
27
28 import java.net.*;
29 import java.text.DateFormat;
30 import java.io.*;
31 import java.util.*;
32
33 /** The JaimConnection object is the primary interface into the Jaim library.
34 * Programs should instantiate a JaimConnection (in most cases the simple constructor should be used).
35 * Once JaimConnection has been instantiated, call {@link #connect} followed by {@link #logIn}.
36 *
37 *
38 * @author paulw
39 * @version $Revision: 1.20 $
40 */
41 public class JaimConnection implements java.lang.Runnable {
42
43 private Socket s;
44 private InputStream sin;
45 private OutputStream sout;
46 private boolean connected;
47 private boolean loggedIn;
48 private boolean loginComplete;
49 private boolean configValid;
50 private String host;
51 private int port;
52 private int clientSequence;
53 private int serverSequence;
54 private ReceiverThread rt;
55 private DeliveryThread dt;
56 private Vector eventListeners;
57 private HashMap watchedBuddies;
58 private HashMap buddies;
59 private HashMap groups;
60 private String nickName;
61 private long lastMessageSendTime;
62 private boolean debug;
63 private Thread myThread;
64 private Vector messageQueue;
65 private boolean exit;
66 private long lastKeepAlive;
67
68 // Number of send "points" - used to control send rate
69 private int sendPoints=10;
70
71
72 private static final int MAX_POINTS=10;
73 private static final int BLOCK_POINTS=5;
74 private static final int POINT_RECOVERY_TIME=2200; // Recover one point every 2.2 seconds
75 private static final int THRESHOLD_DELAY=5000; // Delay when we are threshold of being blocked
76
77 private static final int WAIT_TIME=61000; // Wait 61 secs for a keep alive
78
79 /** Creates new JaimConnection that connects to the default host and port.
80 * In most cases this constructor should be used.
81 */
82 public JaimConnection() {
83 host="toc.oscar.aol.com";
84 port=9898;
85 startMe();
86
87 }
88
89 /** Creates a new Jaim Connection to the specified host/port.
90 * There are currently no reasons to call this constructor, however AOL may change the TOC host and port in the future
91 * @param host The hostname or IP address of the TOC server
92 * @param port The port number to connect to on the host
93 */
94 public JaimConnection(String host,int port) {
95 this.host=host;
96 this.port=port;
97 startMe();
98 }
99
100 /** start the message dispatcher thread
101 */
102
103 private void startMe() {
104 connected=false;
105 loggedIn=false;
106 eventListeners=new Vector();
107 loginComplete=false;
108 lastMessageSendTime=0;
109 watchedBuddies=new HashMap();
110 buddies=new HashMap();
111 groups=new HashMap();
112 debug=false;
113 exit=false;
114 rt=null;
115 configValid=false;
116 lastKeepAlive=System.currentTimeMillis();
117 TocResponseFactory.addResponseHandler(new BuddyUpdateTocResponse());
118 TocResponseFactory.addResponseHandler(new ErrorTocResponse());
119 TocResponseFactory.addResponseHandler(new EvilTocResponse());
120 TocResponseFactory.addResponseHandler(new IMTocResponse());
121 TocResponseFactory.addResponseHandler(new SignOnTocResponse());
122 TocResponseFactory.addResponseHandler(new NickTocResponse());
123 TocResponseFactory.addResponseHandler(new GotoTocResponse());
124 TocResponseFactory.addResponseHandler(new ConfigTocResponse());
125 messageQueue=new Vector();
126 myThread = new Thread(this);
127 myThread.setDaemon(true);
128 myThread.start();
129 dt=new DeliveryThread();
130 dt.setDaemon(true);
131 dt.start();
132 }
133
134
135
136 /** Enable/Disable debugging messages to stdout
137 * @param debug true if debugging messages should be output
138 */
139
140 public void setDebug(boolean debug) {
141 this.debug=debug;
142 }
143
144
145 /** Specify the intermessage delay time. <br>
146 * The {@link #sendIM } method will ensure that at least this amount of time has elapsed between messages
147 * @param msec The delay period in milliseconds
148 * @deprecated This function is no longer used - send throttling is automatic
149 */
150 public void setInterMessageDelay(long msec) {
151
152 }
153
154 /** Get the intermessage delay time
155 * @return The intermessage delay time in milliseconds
156 * @deprecated This function is no longer used
157 */
158 public long getInterMessageDelay() {
159 return(0);
160 }
161
162 /** Set the EventListener object. This object will be notified of incoming TOC events
163 * @param l The listener class to be notified
164 * @deprecated replaced by {@link #addEventListener}
165 */
166 public void setEventListener(JaimEventListener l) throws TooManyListenersException {
167 eventListeners.add(l);
168 }
169
170 /** Add an EventListener object. This object will be notified of incoming TOC events
171 * @param l The listener class to be notified
172 */
173
174 public void addEventListener(JaimEventListener l) {
175 eventListeners.add(l);
176 }
177
178 /** Remove an EventListener object. This object will no longer be notified of incoming TOC events
179 * @param l The listener class to be removed
180 */
181
182 public void removeEventListener(JaimEventListener l) {
183 eventListeners.remove(l);
184 }
185
186
187 /** Initiate a connection to the TOC server
188 * @throws IOException If an underlying network communication fails
189 */
190 public void connect() throws IOException {
191 s=new Socket(host,port);
192 s.setSoTimeout(500);
193 sin=s.getInputStream();
194 sout=s.getOutputStream();
195
196 sout.write("FLAPON\r\n\r\n".getBytes());
197
198 FLAPInputFrame inFrame = new FLAPInputFrame();
199
200 int i=-1;
201
202
203 while (!inFrame.completeFrameRead()) {
204 i=sin.read();
205 inFrame.addFrameData((byte)i);
206 }
207
208 try {
209 FLAPFrame f = FLAPFrameFactory.createFLAPFrame(inFrame.getFrameData());
210 FLAPSignonFrame sf = (FLAPSignonFrame)f;
211 if (debug) {
212 System.out.println("Starting sequence="+sf.getSequence());
213 System.out.println("FLAP version = "+sf.getFLAPVersion());
214 }
215 clientSequence=sf.getSequence();
216 serverSequence=sf.getSequence();
217 }
218 catch (FLAPFrameException e) {
219 throw new IOException("FLAPFrameException:"+e.toString());
220 }
221 if (rt!=null) {
222 rt.pleaseExit();
223 }
224 rt=new ReceiverThread(this);
225 rt.setInputStream(sin);
226 rt.setDaemon(true);
227 rt.start();
228 connected=true;
229 }
230
231 /** Disconnect from the TOC server
232 * @throws IOException if a network transport error occurs
233 */
234 public void disconnect() throws IOException {
235 exit=true;
236 rt.pleaseExit();
237 try {
238 rt.join(700);
239 myThread.join(700);
240 }
241 catch (InterruptedException e) {
242 }
243
244 if (connected) {
245 if (loggedIn) {
246 logOut();
247 }
248 s.close();
249 connected=false;
250 }
251 }
252
253
254 /** Check if the TOC login process has completed
255 * @return true if the login process is complete
256 */
257 public boolean isLoginComplete() {
258 return(loginComplete);
259 }
260
261 /** Log out from the TOC server
262 */
263 public void logOut() {
264 loggedIn=false;
265 loginComplete=false;
266 configValid=false;
267
268 }
269
270 /** Get the formatted Nick Name for this connection. If no formatted nick name has been registered with the TOC server, then the username provided to the logIn call is returned
271 * @return The Nick Name associated with this connection
272 */
273 public String getNickName() {
274 return(nickName);
275 }
276
277 /** login to the TOC server. {@link #connect() } method should be called first
278 * @param username The username to log in with
279 * @param password the password for the specified username
280 * @param waitTime time in milliseconds for successful login before declaring an error
281 * @throws IOException If a network error occurs
282 * @throws JaimException If a login failure occurs or login fails to complete before waittime expires
283 */
284 public void logIn(String username,String password,int waitTime) throws JaimException, IOException {
285 if (connected) {
286
287 nickName=username;
288 String nuser=Utils.normalise(username);
289 FLAPSignonFrame sof = new FLAPSignonFrame();
290 sof.setSequence(clientSequence++);
291 sof.setFLAPVersion(1);
292 sof.setTLVTag(1);
293 sof.setUserName(nuser);
294 sout.write(sof.getFrameData());
295 TocSignonCommand soc=new TocSignonCommand(host,port,username,password);
296 sendTocCommand(soc);
297 for (int i=0;i<waitTime/100;i++) // Wait a max of waitTime * 100ms
298 {
299 if (loginComplete||!connected) // Have we logged in successfully
300 {
301 break; // If so then return
302 }
303 else {
304 try {
305 Thread.sleep(100); //Sleep for a tenth of a second
306 }
307 catch (InterruptedException e) {
308 }
309 }
310 }
311 if (loginComplete) {
312 loggedIn=true;
313 }
314 else {
315 throw new JaimTimeoutException("login failed-timeout waiting for valid response");
316 }
317
318 }
319
320 else
321 throw new JaimStateException("Not connected.");
322 }
323
324 private void sendTocCommand(TocCommand cmd) throws IOException {
325 FLAPDataFrame fr=new FLAPDataFrame();
326 fr.setSequence(nextSequence());
327 if (debug) {
328 System.out.println("Sending "+cmd.toString());
329 }
330 fr.addString(cmd.toString());
331 sout.write(fr.getFrameData());
332 }
333
334 private int nextSequence()
335 {
336 int seq=clientSequence++;
337 if (clientSequence>65535)
338 clientSequence=0;
339 return(seq);
340 }
341
342 private void sendKeepAlive() throws IOException {
343 FLAPKeepAliveFrame fr=new FLAPKeepAliveFrame();
344 fr.setSequence(nextSequence());
345 if (debug) {
346 System.out.println("Sending keepalive");
347 }
348 sout.write(fr.getFrameData());
349 }
350 /** The run method for the dispatcher thread
351 */
352
353 public void run() {
354 while (true) {
355
356 if (messageQueue.size()>0) {
357 realDispatch((FLAPFrame)messageQueue.remove(0));
358 }
359 else {
360 if (System.currentTimeMillis()-lastKeepAlive>WAIT_TIME)
361 {
362 if (debug)
363 {
364 System.out.println("No keepalive received - sending");
365 }
366 try
367 {
368 sendKeepAlive();
369 lastKeepAlive=System.currentTimeMillis();
370 }
371 catch (IOException ioe)
372 {
373 connectionLost();
374 }
375 }
376
377 try {
378 synchronized(this) {
379 this.wait(WAIT_TIME);
380 }
381 }
382 catch (InterruptedException e) {
383 }
384 }
385 }
386 }
387
388 protected void Dispatch(FLAPFrame fr) {
389 messageQueue.addElement(fr);
390 synchronized(this) {
391 this.notify();
392 }
393 }
394
395
396 private void realDispatch(FLAPFrame fr) {
397 switch (fr.getFLAPFrameType()) {
398 case FLAPFrame.FLAP_FRAME_ERROR:
399
400 try {
401 disconnect();
402 }
403 catch (IOException e) {
404 }
405 break;
406 case FLAPFrame.FLAP_FRAME_DATA:
407
408 FLAPDataFrame df=(FLAPDataFrame)fr;
409 TocResponse tr = TocResponseFactory.createResponse(df.getContent());
410 HandleTocResponse(tr);
411 break;
412 case FLAPFrame.FLAP_FRAME_KEEP_ALIVE:
413 if (debug) {
414 System.out.println("Received keep alive frame "+DateFormat.getTimeInstance().format(new Date()));
415 }
416 lastKeepAlive=System.currentTimeMillis();
417 try
418 {
419 sendKeepAlive();
420 }
421 catch (IOException e)
422 {
423 connectionLost();
424 }
425 break;
426 case FLAPFrame.FLAP_FRAME_SIGNOFF:
427 connected=false;
428 loggedIn=false;
429 try {
430 s.close();
431 }
432 catch (IOException e) {
433 }
434 break;
435 default:
436 if (debug) {
437 System.out.println("Unknown type received: "+fr.getFLAPFrameType());
438 }
439 break;
440 }
441 }
442
443
444 protected void HandleTocResponse(TocResponse tr) {
445 if (debug) {
446 System.out.println("Toc Response received:"+tr.toString());
447 }
448 if (tr instanceof SignOnTocResponse) {
449 TocInitDoneCommand tid = new TocInitDoneCommand();
450 TocAddBuddyCommand tab = new TocAddBuddyCommand();
451 Iterator it=watchedBuddies.keySet().iterator();
452 while (it.hasNext()) {
453 tab.addBuddy((String)it.next());
454 }
455 try {
456 sendTocCommand(tab);
457 sendTocCommand(tid);
458 deliverEvent(new LoginCompleteTocResponse()); // nform clients that login processing is now complete
459 loginComplete=true;
460 }
461 catch (IOException e) {
462 }
463 }
464 else if (tr instanceof ConfigTocResponse) {
465 if (debug) {
466 System.out.println("Received ConfigTocResponse");
467 }
468
469 ConfigTocResponse ctr=(ConfigTocResponse)tr;
470 Enumeration e=ctr.enumerateGroups();
471 while (e.hasMoreElements()) {
472 Group g=(Group)e.nextElement();
473 groups.put(g.getName(),g);
474 Enumeration be=g.enumerateBuddies();
475 while (be.hasMoreElements()) {
476 Buddy b=(Buddy)be.nextElement();
477 if (!buddies.containsKey(b.getName())) {
478 buddies.put(b.getName(),b);
479 }
480 }
481 }
482 configValid=true;
483 }
484
485 deliverEvent(tr);
486
487
488
489 }
490
491 /** Deliver a TocResponse event to registered listeners
492 *@param tr The TocResponse to be delivered
493 */
494
495 private void deliverEvent(TocResponse tr) {
496 dt.deliverMessage(tr);
497
498 }
499
500 /** Send an instant message
501 * @param recipient The nickname of the message recipient
502 * @param msg The message to send
503 * @throws IOException if a network error occurs
504 */
505 public void sendIM(String recipient,String msg) throws IOException {
506 sendIM(recipient,msg,false);
507 }
508
509 /** Send an instant message
510 * @param recipient The nickname of the message recipient
511 * @param msg The message to send
512 * @param auto true if this is an automatic response (eg. away message)
513 * @throws IOException if a network error occurs
514 */
515 public void sendIM(String recipient,String msg,boolean auto) throws IOException {
516
517 synchronized(this) {
518
519 if (sendPoints < MAX_POINTS) // If we have less than full points
520 {
521 long now=System.currentTimeMillis();
522 long difference=now-lastMessageSendTime;
523 sendPoints+=(int)(difference/POINT_RECOVERY_TIME); // 1 point is regained every 2 seconds
524 if (sendPoints >MAX_POINTS)
525 sendPoints=MAX_POINTS;
526
527 if (sendPoints <BLOCK_POINTS) // If we are in danger of being limited
528 {
529 try {
530 Thread.sleep(THRESHOLD_DELAY); // Wait until we get one point back
531 sendPoints++;
532 }
533 catch (InterruptedException ie) {
534 }
535 }
536 }
537 }
538 TocIMCommand im=new TocIMCommand(recipient,msg,auto);
539
540 sendTocCommand(im);
541 sendPoints--;
542 if (debug) {
543 System.out.println("Points="+sendPoints);
544 }
545
546 lastMessageSendTime=System.currentTimeMillis();
547 }
548
549
550 /** Add a buddy to a group. This information can be saved on the server by calling {@link #saveConfig}
551 * @param buddyName The normalised buddy name to add
552 * @param groupName The name of the group to add this buddy to
553 * @param pos the position in the group at which to add the buddy.
554 * @return The {@link Buddy} object that represents the specified buddy name.
555 */
556
557 public Buddy addBuddy(String buddyName, String groupName, int pos) {
558
559 if (debug) {
560 System.out.println("Adding "+buddyName+" to group "+groupName+" at position "+pos);
561 }
562
563 Buddy buddy;
564 buddy=(Buddy)buddies.get(buddyName);
565 if (buddy==null) {
566 buddy=new Buddy(buddyName);
567 }
568 Group group=(Group)groups.get(groupName);
569 if (group==null) {
570 group=new Group(groupName);
571 groups.put(groupName,group);
572 }
573 if (pos>group.getBuddyCount()||pos==-1) {
574 group.addBuddy(buddy);
575 }
576 else {
577 group.addBuddy(buddy,pos);
578 }
579 return(buddy);
580 }
581
582 /** Add a buddy to a group. This information can be saved on the server by calling {@link #saveConfig}
583 * The buddy is added to the end of the group
584 * @param buddyName The normalised buddy name to add
585 * @param groupName The name of the group to add this buddy to
586 * @return The {@link Buddy} object that represents the specified buddy name.
587 */
588
589 public Buddy addBuddy(String buddyName, String groupName) {
590 return(addBuddy(buddyName,groupName,-1));
591 }
592
593 /** Add a buddy to the watch list for this connection.
594 * This method must be called after {@link #connect()}
595 * It also appears that the login process will not complete unless at least one buddy is added to the watch list
596 * @param buddy The nickname to add to the watch list
597 * @throws JaimException if the method is called at the wrong time
598 * @see JaimEventListener
599 * @deprecated the {@link #watchBuddy} method should be used instead
600 */
601 public void addBuddy(String buddy) throws JaimException {
602 watchBuddy(buddy);
603
604 }
605
606 /** Add a buddy to the watch list for this connection.
607 * This method must be called after {@link #connect()}
608 * It also appears that the login process will not complete unless at least one buddy is added to the watch list
609 * @param buddy The nickname to add to the watch list
610 * @throws JaimException if the method is called at the wrong time
611 * @see JaimEventListener
612 */
613 public void watchBuddy(String buddy) throws JaimException {
614 if (loggedIn) {
615 try {
616 TocAddBuddyCommand tab = new TocAddBuddyCommand();
617 tab.addBuddy(buddy);
618 sendTocCommand(tab);
619 }
620 catch (IOException e) {
621 throw new JaimException(e.toString());
622 }
623 }
624
625 watchedBuddies.put(buddy,buddy);
626
627 }
628
629 /** Save group/buddy list configuration to the TOC server
630 * @throws IOException if a network error occurs
631 */
632
633 public void saveConfig() throws IOException {
634 TocSetConfigCommand tsc=new TocSetConfigCommand();
635 Iterator it =groups.keySet().iterator();
636 while (it.hasNext()) {
637 Group g = (Group)groups.get(it.next());
638 tsc.addGroup(g);
639 }
640 sendTocCommand(tsc);
641
642 }
643
644 /** Return the set of groups that have been stored in the TOC server
645 * The information returned from this method is only valid if {@link #isConfigValid} returns true
646 * @return A Collection of {@link Group} Objects
647 */
648
649 public Collection getGroups() {
650 return(groups.values());
651 }
652
653 /**
654 * Return a group, given its name
655 * @return A {@link Group} Object corresponding to the string name
656 */
657
658 public Group getGroupBy(String name) {
659 Group result = (Group) groups.get(name);
660 return result;
661 }
662
663
664 /** Indicate whether configuration information has been received from the TOC server.
665 * If this method returns true then the information returned by {@link #getGroups} is valid
666 * @return true if configuration information has been received from the TOC server.
667 */
668
669 public boolean isConfigValid() {
670 return(configValid);
671 }
672
673 /** Send a warning or "Evil" to another user. You must be involved in a communication with a user before you can warn them
674 * @param buddy The nickname of the buddy to warn
675 * @param anonymous true if the warning should be sent anonymously
676 * @throws IOException if a network error occurs
677 */
678 public void sendEvil(String buddy,boolean anonymous) throws IOException {
679 TocEvilCommand ec=new TocEvilCommand(buddy,anonymous);
680 sendTocCommand(ec);
681 }
682
683
684 /** Set the information for the logged in user
685 * @param information The information for this user (May contain HTML)
686 * @throws IOException if a network error occurs
687 */
688 public void setInfo(String information) throws IOException {
689 TocSetInfoCommand sic=new TocSetInfoCommand(information);
690 sendTocCommand(sic);
691 }
692
693 /** Get the information for the specified user
694 * @param username The screenname for whom info is requested (May contain HTML)
695 * @throws IOException if a network error occurs
696 */
697 public void getInfo(String username) throws IOException {
698 TocGetInfoCommand gic=new TocGetInfoCommand(username);
699 sendTocCommand(gic);
700 }
701
702 /** Get an Input stream associated with a URL returned by the "GOTO_URL" toc response
703 *@param file The "file" returned by calling GotoTocResponse#getURL
704 *@return An InputStream connected to the specified URL
705 *@throws IOException if an IO error occurs
706 *@throws MalformedURLException if there is an error building the URL
707 */
708
709
710 public InputStream getURL(String file) throws IOException, MalformedURLException {
711 URL URL;
712
713 URL=new URL("http",host,port,file);
714
715 return(URL.openStream());
716 }
717
718
719
720 /** Set the information for the logged in user
721 * @param awayMsg The away message for this user. May contain HTML. To cancel "away" status set the awayMsg to ""
722 * @throws IOException if a network error occurs
723 */
724 public void setAway(String awayMsg) throws IOException {
725 TocSetAwayCommand sic=new TocSetAwayCommand(awayMsg);
726 sendTocCommand(sic);
727 }
728
729
730 /** Adds the specified buddy to your permit list.
731 * @param buddy The buddy to add to your block list. If this is an empty string, mode is changed to "permit none"
732 * @throws JaimException if a network error occurs
733 */
734 public void addPermit(String buddy) throws JaimException {
735 if (loggedIn) {
736 try {
737 TocAddPermitCommand tap = new TocAddPermitCommand();
738 tap.addPermit(buddy);
739 sendTocCommand(tap);
740 }
741 catch (IOException e) {
742 throw new JaimException(e.toString());
743 }
744 }
745 }
746
747 /** Adds the specified buddy to your block list.
748 * @param buddy The buddy to add to your block list. If this is an empty string, mode is changed to "deny none"
749 * @throws JaimException if a network error occurs
750 */
751 public void addBlock(String buddy) throws JaimException {
752 if (loggedIn) {
753 try {
754 TocAddDenyCommand tad = new TocAddDenyCommand();
755 tad.addDeny(buddy);
756 sendTocCommand(tad);
757 }
758 catch (IOException e) {
759 throw new JaimException(e.toString());
760 }
761 }
762 }
763
764 /** Called by receiver thread to indicate that the connection has been terminated by an IOException
765 */
766
767 private void connectionLost() {
768 deliverEvent(new ConnectionLostTocResponse());
769 logOut();
770 connected=false;
771 }
772
773
774
775 /** Set the idle time for this user
776 * @param idleSecs The number of seconds the user has been idle for. Set to 0 to indicate current activity. The server will increment the idle time if non-zero
777 * @throws IOException if a network error occurs
778 */
779 public void setIdle(int idleSecs) throws IOException {
780 TocSetIdleCommand sic=new TocSetIdleCommand(idleSecs);
781 sendTocCommand(sic);
782 }
783
784
785 /** Delete a buddy from the buddy watch list. The buddy should have been added with {@link #addBuddy } first.
786 * The buddy list can only be modified after {@link #connect } is called.
787 * @param buddy The buddy name to be deleted\
788 * @deprecated use {@link #unwatchBuddy } instead
789 */
790 public void deleteBuddy(String buddy) {
791 unwatchBuddy(buddy);
792 }
793
794 /** Delete a buddy from the buddy watch list. The buddy should have been added with {@link #addBuddy } first.
795 * The buddy list can only be modified after {@link #connect } is called.
796 * @param buddy The buddy name to be deleted
797 */
798 public void unwatchBuddy(String buddy) {
799 watchedBuddies.remove(buddy);
800 }
801
802 private class ReceiverThread extends Thread {
803 private InputStream sin;
804 private boolean exit;
805 private JaimConnection parent;
806
807 private ReceiverThread(JaimConnection parent) {
808 this.parent=parent;
809 exit=false;
810 }
811
812 private void setInputStream(InputStream in) {
813 sin=in;
814 }
815
816 public void run() {
817 if (debug) {
818 System.out.println("Receiver starting");
819 }
820 FLAPInputFrame inframe=new FLAPInputFrame();
821 try {
822 while (!exit) {
823 try {
824 int i;
825 while ( !inframe.completeFrameRead()) {
826 i=sin.read();
827 inframe.addFrameData((byte)i);
828 }
829 try {
830 FLAPFrame fr=FLAPFrameFactory.createFLAPFrame(inframe.getFrameData());
831 parent.Dispatch(fr);
832 }
833 catch (FLAPFrameException ffe) {
834 if (debug) {
835 ffe.printStackTrace();
836 }
837 }
838 if (inframe.completeFrameRead()) {
839 inframe.resetInputFrame();
840 }
841 }
842 catch (InterruptedIOException iie) {
843 // We expect these because we are performing reads with a timeout
844 }
845 }
846 }
847 catch (IOException e) {
848 connectionLost(); // Indicate that we have lost our connection
849 if (debug) {
850 e.printStackTrace();
851 }
852 }
853 }
854
855 private void pleaseExit() {
856 exit=true;
857 }
858
859 }
860 private class DeliveryThread extends Thread {
861 private Vector messages;
862 private boolean exit;
863 private DeliveryThread() {
864 messages=new Vector();
865 exit=false;
866 }
867
868 private void deliverMessage(TocResponse tr) {
869 synchronized(this) {
870 messages.add(tr);
871 this.notify();
872 }
873 }
874
875 public void run() {
876 if (debug) {
877 System.out.println("Delivery Thread starting");
878 }
879 while (!exit) {
880 if (messages.size()>0) {
881 TocResponse tr=(TocResponse)messages.remove(0);
882 doDelivery(tr);
883 }
884 else {
885 synchronized(this) {
886 try
887 {
888 this.wait();
889 }
890 catch (InterruptedException e)
891 {
892 }
893 }
894 }
895
896 }
897 }
898
899 private void doDelivery(TocResponse tr) {
900 for (int i=0;i<eventListeners.size();i++) {
901 JaimEventListener el=(JaimEventListener)eventListeners.elementAt(i);
902 el.receiveEvent(new JaimEvent(this,tr));
903 }
904 }
905
906 private void pleaseExit() {
907 exit=true;
908 }
909
910
911 }
912
913
914 }