001: package org.millstone.examples;
002:
003: import java.io.*;
004: import java.util.*;
005: import java.lang.ref.WeakReference;
006:
007: import org.millstone.base.terminal.StreamResource;
008: import org.millstone.base.ui.*;
009:
010: /** Chat example application.
011: *
012: * <p>This example application implements Internet chatroom with the
013: * following features:
014: * <ul>
015: * <li>Continuosly streaming chat discussion. This is implemented
016: * with StreamResource that is kept open during the discussion.
017: * <li>Dynamically changing frames.
018: * <li>Chatroom that is implemented with static list of chatters
019: * referenced by weak references.
020: * </ul>
021: * </p>
022: *
023: * @see org.millstone.base.Application
024: * @see org.millstone.base.ui.FrameWindow
025: * @see org.millstone.base.terminal.StreamResource
026: */
027:
028: public class Chat extends org.millstone.base.Application implements
029: StreamResource.StreamSource, Button.ClickListener {
030:
031: /** Linked list of Chat applications who participate the discussion */
032: private static LinkedList chatters = new LinkedList();
033:
034: /** Reference (to this application) stored in chatters list */
035: private WeakReference listEntry = null;
036:
037: /** Writer for writing to open chat stream */
038: private PrintWriter chatWriter = null;
039:
040: /** Login name / Alias for chat */
041: private TextField loginName = new TextField("Your name?", "");
042:
043: /** Login button */
044: private Button loginButton = new Button("Enter chat");
045:
046: /** Text to be said to discussion */
047: private TextField sayText = new TextField();
048:
049: /** Button for sending the sayTest to discussion */
050: private Button say = new Button("Say");
051:
052: /** Button for listing the people in the chatroom */
053: private Button listUsers = new Button("List chatters");
054:
055: /** Last time this chat application said something */
056: private long idleSince = (new Date()).getTime();
057:
058: /** framewindow for following the discussion and control */
059: FrameWindow frames = new FrameWindow("Millstone chat");
060:
061: /** Last messages */
062: private static LinkedList lastMessages = new LinkedList();
063:
064: /** Initialize the chat application */
065: public void init() {
066:
067: // Initialize user interface
068: say.dependsOn(sayText);
069: say.addListener((Button.ClickListener) this );
070: listUsers.addListener((Button.ClickListener) this );
071: StreamResource chatStream = new StreamResource(this ,
072: "discussion.html", this );
073: chatStream.setBufferSize(1);
074: chatStream.setCacheTime(0);
075: frames.getFrameset().newFrame(chatStream, "chatDiscussion");
076: Window controls = new Window("", new OrderedLayout(
077: OrderedLayout.ORIENTATION_HORIZONTAL));
078: controls.setName("chatControls");
079: controls.addComponent(sayText);
080: sayText.setColumns(40);
081: controls.addComponent(say);
082: controls.addComponent(loginName);
083: loginName.focus();
084: controls.addComponent(loginButton);
085: loginButton.dependsOn(loginName);
086: loginButton.addListener(this );
087: controls.addComponent(listUsers);
088: Button leaveButton = new Button("Leave", this , "leave");
089: controls.addComponent(leaveButton);
090: say.setVisible(false);
091: sayText.setVisible(false);
092: frames.getFrameset().newFrame(controls).setAbsoluteSize(60);
093: frames.getFrameset().setVertical(true);
094: frames.setName("chatMain");
095: setMainWindow(frames);
096:
097: // Register chat application
098: synchronized (chatters) {
099: chatters.add(listEntry = new WeakReference(this ));
100: }
101: }
102:
103: /** Handle button actions for login, user listing and saying */
104: public void buttonClick(Button.ClickEvent event) {
105:
106: // Say something in discussion
107: if (event.getSource() == say && sayText.toString().length() > 0) {
108:
109: // Say something to chatstream
110: say("<b>" + getUser() + ": </b>" + sayText + "<br>");
111:
112: // Clear the saytext field
113: sayText.setValue("");
114: sayText.focus();
115: }
116:
117: // List the users
118: else if (event.getSource() == listUsers)
119: listUsers();
120:
121: // Login to application
122: else if (event.getSource() == loginButton
123: && loginName.toString().length() > 0) {
124:
125: // Set user name
126: setUser(loginName.toString());
127:
128: // Hide logins controls
129: loginName.setVisible(false);
130: loginButton.setVisible(false);
131:
132: // Show say controls
133: say.setVisible(true);
134: sayText.setVisible(true);
135: sayText.focus();
136:
137: // Announce discussion joining
138: say("<i>" + getUser() + " joined the discussion ("
139: + (new Date()).toString() + ")</i><br>");
140: }
141: }
142:
143: /** List chatters to chat stream */
144: private void listUsers() {
145:
146: // Compose userlist
147: StringBuffer userlist = new StringBuffer();
148: userlist
149: .append("<div style=\"background-color: #ffffd0;\"><b>Chatters ("
150: + (new Date()) + ")</b><ul>");
151: synchronized (chatters) {
152: for (Iterator i = chatters.iterator(); i.hasNext();) {
153: try {
154: Chat c = (Chat) ((WeakReference) i.next()).get();
155: String name = (String) c.getUser();
156: if (name != null && name.length() > 0) {
157: userlist.append("<li>" + name);
158: userlist
159: .append(" (idle "
160: + ((new Date()).getTime() - c.idleSince)
161: / 1000 + "s)");
162: }
163: } catch (NullPointerException ignored) {
164: }
165: }
166: }
167: userlist
168: .append("</ul></div><script>self.scroll(0,71234);</script>\n");
169:
170: // Print the user list to chatstream
171: printToStream(userlist.toString());
172: }
173:
174: /** Print to chatstream and scroll the window */
175: private void printToStream(String text) {
176: if (chatWriter != null) {
177: chatWriter.println(text);
178: chatWriter
179: .println("<script>self.scroll(0,71234);</script>\n");
180: chatWriter.flush();
181: }
182: }
183:
184: /** Say to all chat streams */
185: private void say(String text) {
186:
187: // Get all the listeners
188: Object[] listener;
189: synchronized (chatters) {
190: listener = chatters.toArray();
191: }
192:
193: // Put the saytext to listener streams
194: // Remove dead listeners
195: for (int i = 0; i < listener.length; i++) {
196: Chat c = (Chat) ((WeakReference) listener[i]).get();
197: if (c != null)
198: c.printToStream(text);
199: else
200: chatters.remove(listener[i]);
201: }
202:
203: // Update idle time
204: idleSince = (new Date()).getTime();
205:
206: // Update last messages
207: synchronized (lastMessages) {
208: lastMessages.addLast(text);
209: while (lastMessages.size() > 5)
210: lastMessages.removeFirst();
211: }
212: }
213:
214: /** Open chat stream */
215: public InputStream getStream() {
216:
217: // Close any existing streams
218: if (chatWriter != null)
219: chatWriter.close();
220:
221: // Create piped stream
222: PipedOutputStream chatStream = new PipedOutputStream();
223: chatWriter = new PrintWriter(chatStream);
224: InputStream is = null;
225: try {
226: is = new PipedInputStream(chatStream);
227: } catch (IOException ignored) {
228: chatWriter = null;
229: return null;
230: }
231: ;
232:
233: // Write headers
234: printToStream("<html><head><title>Discussion " + (new Date())
235: + "</title>" + "</head><body>\n");
236:
237: // Print last messages
238: Object[] msgs;
239: synchronized (lastMessages) {
240: msgs = lastMessages.toArray();
241: }
242: for (int i = 0; i < msgs.length; i++)
243: printToStream(msgs[i].toString());
244:
245: // Allways list the users
246: listUsers();
247:
248: return is;
249: }
250:
251: /** Leave the chat */
252: public void leave() {
253:
254: // If we have been logged in, say goodbye
255: if (listEntry != null) {
256: if (getUser() != null)
257: say("<i>" + getUser() + " left the chat ("
258: + (new Date()) + ")</i><br>");
259:
260: synchronized (chatters) {
261: chatters.remove(listEntry);
262: listEntry = null;
263: }
264: }
265: if (chatWriter != null)
266: chatWriter.close();
267:
268: // Close the chat frames
269: if (frames != null) {
270: frames.getFrameset().removeAllFrames();
271: Window restartWin = new Window();
272: frames.getFrameset().newFrame(restartWin);
273: restartWin.addComponent(new Button("Restart chat", this ,
274: "close"));
275: frames = null;
276: }
277: }
278:
279: /** Make sure that everybody leaves the chat */
280: public void finalize() {
281: leave();
282: }
283:
284: }
285: /* This Millstone sample code is public domain. *
286: * For more information see www.millstone.org. */
|