001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package echo2example.chatclient;
031:
032: import java.io.IOException;
033:
034: import nextapp.echo2.app.ApplicationInstance;
035: import nextapp.echo2.app.TaskQueueHandle;
036: import nextapp.echo2.app.Window;
037: import nextapp.echo2.app.event.ActionEvent;
038: import nextapp.echo2.app.event.ActionListener;
039: import nextapp.echo2.webcontainer.ContainerContext;
040:
041: /**
042: * Chat Client <code>ApplicationInstance</code> implementation.
043: */
044: public class ChatApp extends ApplicationInstance {
045:
046: /**
047: * Returns the active <code>ChatApp</code>.
048: *
049: * @return the active <code>ChatApp</code>
050: */
051: public static ChatApp getApp() {
052: return (ChatApp) getActive();
053: }
054:
055: private ChatSession chatSession;
056: private TaskQueueHandle incomingMessageQueue;
057:
058: // Auto-logout times set very short for demonstration purposes.
059: private static final int POST_INTERVAL_AUTO_LOGOUT_WARNING = 2 * 60 * 1000; // 2 minutes
060: private static final int POST_INTERVAL_AUTO_LOGOUT = 3 * 60 * 1000; // 3 minutes
061:
062: private long lastActionTime;
063: private long lastPostTime;
064: private int pollingInterval = 1000;
065:
066: private MessageDialog logoutWarningDialog;
067:
068: /**
069: * Calculates the appropriate client-server polling interval based on the
070: * delta between the current time and the last interesting event (i.e.
071: * posted message in the chat) which occurred.
072: *
073: * @return the appropriate polling interval
074: */
075: private int calculatePollingInterval() {
076: long delta = System.currentTimeMillis() - lastActionTime;
077: if (delta < 10 * 1000) {
078: // Last action 0-10 seconds ago: 1 second poll update intervals.
079: return 1000;
080: } else if (delta < 20 * 1000) {
081: // Last action 10-20 seconds ago: 2 second poll update intervals.
082: return 2000;
083: } else if (delta < 30 * 1000) {
084: // Last action 20-30 seconds ago: 3 second poll update intervals.
085: return 3000;
086: } else if (delta < 60 * 1000) {
087: // Last action 30-60 seconds ago: 5 second poll update intervals.
088: return 5000;
089: } else {
090: // Last action > 60 seconds ago: 10 second poll update intervals.
091: return 10000;
092: }
093: }
094:
095: /**
096: * Attempts to connect to the chat server with the specified user name.
097: * Displays a <code>ChatScreen</code> for the user if the user
098: * is successfully connected. Performs no action if the user is not
099: * successfully connected.
100: *
101: * @return true if the operation was successfully completed,
102: * false otherwise
103: */
104: public boolean connect(String userName) {
105: try {
106: chatSession = ChatSession.forUserName(userName);
107: } catch (IOException ex) {
108: throw new RuntimeException(ex);
109: }
110: if (chatSession == null) {
111: return false;
112: } else {
113: if (incomingMessageQueue != null) {
114: throw new IllegalStateException();
115: }
116: incomingMessageQueue = createTaskQueue();
117: updatePollingInterval(true);
118: lastPostTime = System.currentTimeMillis();
119:
120: getDefaultWindow().setContent(new ChatScreen());
121: return true;
122: }
123: }
124:
125: /**
126: * Disconnects from the chat server and logs the current user out.
127: * Displays the <code>LoginScreen</code>.
128: */
129: public void disconnect() {
130: try {
131: chatSession.dispose();
132: chatSession = null;
133: if (incomingMessageQueue != null) {
134: removeTaskQueue(incomingMessageQueue);
135: incomingMessageQueue = null;
136: }
137: logoutWarningDialog = null;
138: getDefaultWindow().setContent(new LoginScreen());
139: } catch (IOException ex) {
140: throw new RuntimeException(ex);
141: }
142: }
143:
144: /**
145: * Retrieves new messages from the <code>ChatSession</code>.
146: * Once the new messages are deleted they are removed from the queue of
147: * 'new' messages.
148: * Invoking this method thus alters the state of the new message queue.
149: *
150: * @return an array of new messages
151: */
152: public ChatSession.Message[] getNewMessages() {
153: return chatSession.getNewMessages();
154: }
155:
156: /**
157: * Returns the user name of the currently logged-in user.
158: *
159: * @return the user name
160: */
161: public String getUserName() {
162: return chatSession == null ? null : chatSession.getUserName();
163: }
164:
165: /**
166: * The <code>hasQueuedTasks()</code> method has been overridden such that we
167: * can perform checks at every polling interval. Alternatively tasks could
168: * be added using threads running in the background, but for compliance
169: * with earlier versions of the J2EE specification which do not allow
170: * multi-threading, such work is accomplished in this method.
171: *
172: * @see nextapp.echo2.app.ApplicationInstance#hasQueuedTasks()
173: */
174: public boolean hasQueuedTasks() {
175: // Poll server and determine if any new messages have been posted.
176: if (pollServer()) {
177: // Add new messages to ChatScreen.
178: final ChatScreen chatScreen = (ChatScreen) getDefaultWindow()
179: .getContent();
180: enqueueTask(incomingMessageQueue, new Runnable() {
181: public void run() {
182: chatScreen.updateMessageList();
183: updatePollingInterval(true);
184: }
185: });
186: }
187:
188: // Determine if the polling interval should be updated, and if
189: // necessary, queue a task to update it.
190: if (pollingInterval != calculatePollingInterval()) {
191: enqueueTask(incomingMessageQueue, new Runnable() {
192: public void run() {
193: updatePollingInterval(false);
194: }
195: });
196: }
197:
198: if (System.currentTimeMillis() - lastPostTime > POST_INTERVAL_AUTO_LOGOUT) {
199: // If the user has not posted any messages in a period of
200: // time, automatically log the user out.
201: enqueueTask(incomingMessageQueue, new Runnable() {
202: public void run() {
203: disconnect();
204: }
205: });
206: } else if (System.currentTimeMillis() - lastPostTime > POST_INTERVAL_AUTO_LOGOUT_WARNING) {
207: // If the user has not posted any messages in a period of
208: // time, raise a dialog box to warn him/her that s/he may
209: // soon be automatically logged out.
210: enqueueTask(incomingMessageQueue, new Runnable() {
211: public void run() {
212: if (logoutWarningDialog == null) {
213: logoutWarningDialog = new MessageDialog(
214: Messages
215: .getString("AutoLogoutWarningDialog.Title"),
216: Messages
217: .getString("AutoLogoutWarningDialog.Message"),
218: MessageDialog.TYPE_CONFIRM,
219: MessageDialog.CONTROLS_OK);
220: getDefaultWindow().getContent().add(
221: logoutWarningDialog);
222: logoutWarningDialog
223: .addActionListener(new ActionListener() {
224:
225: /**
226: * Reset last post time if user engages the dialog.
227: *
228: * @see nextapp.echo2.app.event.ActionListener#actionPerformed(nextapp.echo2.app.event.ActionEvent)
229: */
230: public void actionPerformed(
231: ActionEvent e) {
232: lastPostTime = System
233: .currentTimeMillis();
234: logoutWarningDialog = null;
235: }
236: });
237: }
238: }
239: });
240: }
241:
242: return super .hasQueuedTasks();
243: }
244:
245: /**
246: * @see nextapp.echo2.app.ApplicationInstance#init()
247: */
248: public Window init() {
249: setStyleSheet(Styles.DEFAULT_STYLE_SHEET);
250: Window window = new Window();
251: window.setTitle(Messages.getString("Application.Title.Window"));
252: window.setContent(new LoginScreen());
253: return window;
254: }
255:
256: /**
257: * Polls the <code>Server</code> to determine if any new messages are
258: * present.
259: *
260: * @return true if any new messages are present
261: */
262: private boolean pollServer() {
263: if (chatSession == null) {
264: return false;
265: }
266: try {
267: chatSession.pollServer();
268: return chatSession.hasNewMessages();
269: } catch (IOException ex) {
270: throw new RuntimeException(ex);
271: }
272: }
273:
274: /**
275: * Posts a message to the chat server for the logged-in user.
276: *
277: * @param content the content of the message to post
278: */
279: public void postMessage(String content) {
280: try {
281: chatSession.postMessage(content);
282: updatePollingInterval(true);
283: lastPostTime = System.currentTimeMillis();
284: } catch (IOException ex) {
285: throw new RuntimeException(ex);
286: }
287: }
288:
289: /**
290: * Updates the client-server polling interval based on the time since
291: * the last event of interest. The interval is increased when nothing
292: * interesting appears to be occurring.
293: *
294: * @param reset flag indicating whether an action has occurred, if true,
295: * the current time will be marked as the time of the last action
296: * and used in future calculations of polling interval.
297: */
298: private void updatePollingInterval(boolean reset) {
299: if (reset) {
300: lastActionTime = System.currentTimeMillis();
301: }
302: pollingInterval = calculatePollingInterval();
303: ContainerContext containerContext = (ContainerContext) getContextProperty(ContainerContext.CONTEXT_PROPERTY_NAME);
304: containerContext.setTaskQueueCallbackInterval(
305: incomingMessageQueue, pollingInterval);
306: }
307: }
|