Example Applet: ChatApplet


Presented below is an example SmartSockets applet. It implements a simple multi-user, real-time chat system using RTserver running on the machine from which the applet was downloaded. When first run, the applet prompts the user for a name. Once the name has been entered, the chat screen is displayed (a large text area for received messages and a small text field for entering messages). The source code is:

// ChatApplet.java 
// Example applet: multiuser real-time chat 
 
1 import java.util.Vector; 
2 import java.applet.Applet; 
3 import java.awt.*; 
4 import java.awt.event.*; 
5 import com.smartsockets.*; 
 
6 public class ChatApplet extends Applet 
7 implements TipcProcessCb, Runnable, ActionListener {  
 
8   static final String info = "Multiuser real-time chat applet 
                                  demo."; 
9   static final int JAVA_CHAT_MT = 8081; 
10  static final String chat_subject = "/java_chat"; 
 
11  TipcSrv srv; 
12  Thread reader = null; 
 
13  CardLayout cl = new CardLayout(); 
14  TextArea out; 
15  TextField in, name; 
16  Button go_btn; 
17  Panel c1, c2; 
 
18  TipcMt chat_mt;   // message type 
19  TipcMsg chat_msg; // message (will always be reused) 
20  TipcCb the_cb; 
   
21  Font my_font = new Font("Serif", Font.PLAIN, 11); 
22  String my_name; 
 
23  public void run() { 
24    Thread me = Thread.currentThread(); 
25    me.setPriority(Thread.MIN_PRIORITY); 
26    while (reader == me) { 
        try { 
27        TipcSrv srv = TipcSvc.getSrv(); 
28        srv.mainLoop(1.0); 
29      } catch (TipcException te) { } 
        try { 
30        Thread.sleep(100); 
31      } catch (InterruptedException ie) { 
32        break;  
        } // catch 
      } // reader 
    } // run 
 
33  public void init() { 
34    String host = getDocumentBase().getHost(); 
35    try { 
36      if (!host.equals("")) { 
37        Tut.setOption("ss.server_names", host); 
       } // if 
38   } catch (TipcException e) {} 
   
     try { 
39     srv = TipcSvc.getSrv(); 
40     srv.create(); 
41   } catch (TipcException e) {} 
     
     try { 
42     chat_mt = TipcSvc.createMt("java_chat", JAVA_CHAT_MT,  
                                    "str str"); 
43   } catch (TipcException e) { 
       // after a reload, the create will throw an exception. since 
       // we want a handle to the mt, must look it up... 
44     chat_mt = TipcSvc.lookupMt(JAVA_CHAT_MT); 
     } // catch 
       
45   chat_msg = TipcSvc.createMsg(chat_mt); 
46   chat_msg.setDest(chat_subject); 
 
     try { 
47     srv.setSubjectSubscribe(chat_subject, true); 
48   } catch (TipcException e) {} 
 
49   the_cb = srv.addProcessCb(this, chat_mt, null); 
     
50   setupGUI(); 
   } // init 
 
51  public void destroy() { 
      try { 
52      srv.removeProcessCb(the_cb); 
        // user prop=-1 means to announce we’ve left the chat 
53      chat_msg.setUserProp(-1); 
54      announce(my_name, chat_subject); 
55    } catch (TipcException e) {} 
    } // destroy 
 
56  void setupGUI() { 
57    setLayout(cl); 
58    c1 = new Panel(); 
59    c1.add(new Label("Enter your name: ")); 
60    name = new TextField("", 16); 
61    c1.add(name); 
62    go_btn = new Button("Go"); 
63    go_btn.addActionListener(this); 
64    c1.add(go_btn); 
65    add("name", c1); 
 
66    c2 = new Panel(new BorderLayout()); 
67    out = new TextArea("", 10, 60,  
                       out.SCROLLBARS_VERTICAL_ONLY); 
68    out.setFont(my_font); 
69    out.setEditable(false); 
70    c2.add(new Label("Message Window"), "North"); 
71    c2.add(out, "Center"); 
72    Panel p = new Panel(); 
73    p.add(new Label("Text to send:")); 
74    in = new TextField("", 60); 
75    in.setFont(my_font); 
76    p.add(in); 
77    Button do_chat = new Button("Send"); 
78    do_chat.addActionListener(this); 
79    p.add(do_chat); 
80    c2.add(p, "South"); 
81    add("chat", c2); 
 
82    cl.show(this, "name"); 
83    name.requestFocus(); 
    } // setupGUI 
84  public void actionPerformed(ActionEvent ae) { 
85    if (ae.getActionCommand().equals("Go") && 
86    name.getText().length()>0) { 
87      my_name = name.getText(); 
88      cl.show(this, "chat"); 
89      in.requestFocus(); 
        // request replies to our announcement with user prop=1 
90      chat_msg.setUserProp(1); 
91      announce(my_name, chat_subject); 
      } // if it's the go button 
92    if (ae.getActionCommand().equals("Send") && 
        in.getText().length()>0) { 
        try { 
93        chat_msg.setNumFields(0); 
94        chat_msg.appendStr(my_name); 
95        chat_msg.appendStr(in.getText()); 
96        srv.send(chat_msg); 
97        srv.flush(); 
        } 
98      catch (TipcException te) { 
99        Tut.warning(te); 
        } // catch 
100     in.setText(""); 
101     in.requestFocus(); 
      } // if it's the send button 
    } // actionPerformed 
 
102 public String getAppletInfo() { 
103   return info; 
    } // getAppletInfo 
 
104 public void stop() { 
105   reader = null; 
    } // stop 
 
106 public void start() { 
107   reader = new Thread(this); 
108   reader.start(); 
109   repaint(); 
    } // start 
 
110 void announce(String name, String dest) { 
     // let the new person know we're here or announce ourselves 
     try { 
111    chat_msg.setDest(dest); 
112    chat_msg.setNumFields(0); 
113    chat_msg.appendStr(my_name); 
114    chat_msg.appendStr(""); 
115    srv.send(chat_msg); 
116    srv.flush(); 
117    chat_msg.setDest(chat_subject); 
     } 
118  catch (TipcException e) { 
119    Tut.warning(e); 
     } // catch 
    } // announce 
 
120 public void process(TipcMsg msg, Object o) { 
      try { 
121     msg.setCurrent(0); 
122     String who = msg.nextStr(); 
123     String text = msg.nextStr(); 
       
        // see if it's an announcement 
124     if (text.equals("")) { 
125       if (msg.getUserProp() == -1) { 
126          out.append("* Leaving chat: " + who + " *"); 
          else { 
127         out.append("* Joining chat: " + who + " *");           
          } 
128       if (!who.equals(my_name) && msg.getUserProp() == 1) { 
129          chat_msg.setUserProp(0); 
130          announce(who, msg.getSender()); 
          } // if "wants replies" flag is set and not our message 
        } // if an announcement 
        else { 
           // a 'regular' message; display it 
131        out.append("[" + who + "] " + text); 
        } // if 
132   } catch (TipcException e) { 
133     Tut.warning(e); 
      } // catch 
    } // process 
 
   } // main class 

Let’s examine some of the key parts of the applet:

Line 12
Declares the class-level variable reader, which will be used to control the messaging thread.
Lines 23-32
Implement the messaging thread. Notice that the thread processes messages for one second (line 28) and then sleeps for 0.1 second (line 30) repeatedly.
Lines 33-38
Sets the ss.server_names option to point back to the server we downloaded from, because the default applet security does not allow us to connect to a local RTserver. In many applet situations it’s very likely there is no RTserver available except on the machine from which the applet was downloaded.
Lines 45-46
Creates the template message for chatting that will be reused during the life of this process.
Lines 51-55
Provides the destroy method, called when the applet is reloaded or shut down. Unregisters our message processing callback and sends out a final "leaving chat" message, via announce. The receiving chat clients will know this is a parting announcement by checking the message’s user property, which is set here to -1.
Lines 56-83
Build the applet’s GUI. Two panels are used, one containing the login screen, the other for the chat and input window. They are arranged with a CardLayout, the login screen displayed "on top" first.
Lines 84-91
If the button on the login screen is pressed, the user name is recorded for inclusion in future outgoing messages (variable my_name), and an announce message is sent with the user property set to 1. The user property is used in this instance to request that other chat clients reply directly back to this client after the announcement, announcing themselves. In this way a new addition to the chat "room" will receive an initial round of announcements from all other clients currently present.
Lines 92-101
When the "send message" button is pressed, this code first clears the message’s old contents (line 93). It then builds the SmartSockets message to be published, publishes it, and flushes the server connection to ensure timely delivery.
Line 105
When an applet’s stop method is called, it is desirable to disable the messaging thread, so the reader variable is invalidated.
Lines 107-108
Upon starting an applet, the messaging thread is created and started.
Lines 110-119
The announce method is used to send a SmartSockets chatting message announcing one of three events: a client joining the conversation, a client leaving the conversation, or this client’s presence in response to a new client’s entry. Each type of announcement contains our name as the first string field and a blank string as the second field.
Line 121
Sets the message’s current field pointer to 0, the first field, to prepare the message for field extraction.
Lines 125-127
Display the appropriate message for clients joining/departing the discussion.
Lines 128-130
If the announcement was to declare a client joining the conversation, requesting a reply from others in the "room" (and it wasn’t generated by this client), send a reply announcement directly to the requesting client.
Line 131
Displays the contents of a general chat message sent by a client (including this client).

As with all applets, we need to create an HTML file to facilitate its download and launch. This is a listing of ChatApplet.html (note that the listing is a "standard" applet invocation; for use with the Java Plug-In, additional tags are required):

1 <html> 
2 <head><title>SmartSockets Chat Applet</title></head> 
3 <body> 
4   <h1>SmartSockets Chat Applet</h1><hr> 
5   <applet  
6   code="ChatApplet.class"  
7   width=600 height=400 
8   archive="ss.jar"> 
9     (SmartSockets Applet) 
10  </applet> 
11  </body> 
12 </html> 

Items to note about the above HTML are:

Line 5
Begins the <applet> tag.
Line 6
Specifies the class that will be run by this applet.
Line 7
Specifies the on-screen size of the applet.
Line 8
Indicates that the ss.jar SmartSockets archive is to be "preloaded". Note that this file must be in the same directory as the compiled .class file, whether using appletviewer or viewing via a web browser.
If you do not wish to copy the ss.jar file during testing with appletviewer, simply leave off the archive modifier until these files are placed on a web server.
Your CLASSPATH setting should allow appletviewer to locate the SmartSockets archive.
Line 9
Alternate text, to be displayed in browsers that do not support Java, but do understand the <applet> tag.

To see the applet in action, use this procedure:

Step 1

Ensure RTserver is running

Make sure RTserver is running. If not, start it:

$ rtserver 

On platforms that support both 32- and 64-bit, use the rtserver64 command to run the 64-bit version of the rtserver script.

Step 2

Compile the ChatApplet.java program

Compile the ChatApplet.java program:

$ javac ChatApplet.java 

Step 3

Start the chat applet program

Start the chat applet:

$ appletviewer ChatApplet.html 

The applet’s window should first display the log-in screen as shown in Figure 7.

Figure 7 Applet Viewer display of ChatApplet (login phase)

Once the user is logged in and a conversation is started, the view from appletviewer might look like Figure 8 below:

Figure 8
Applet Viewer display of ChatApplet (chat phase)

The other side of the conversation (in this case there are only two chatters, but there could be very many) might look like Figure 9 if the user were using a web browser instead of an appletviewer.

Figure 9 Browser display of ChatApplet

As you can see, the two users are carrying on a conversation using SmartSockets messaging. The web server at which the browser is pointed, trojan, happens to be the same machine on which appletviewer is running. This does not need to be the case, however; as long as trojan has RTserver running (for the applet to connect back to) and the appletviewer instance connects to an RTserver process in the same server cloud as trojan’s, messages will be successfully delivered.


TIBCO SmartSockets™ Java Library User’s Guide and Tutorial
Software Release 6.8, July 2006
Copyright © TIBCO Software Inc. All rights reserved
www.tibco.com