Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }