001 package com.croftsoft.apps.chat.client;
002
003 import java.io.*;
004 import java.net.*;
005 import java.util.*;
006
007 import com.croftsoft.core.io.SerializableCoder;
008 import com.croftsoft.core.lang.NullArgumentException;
009 import com.croftsoft.core.lang.lifecycle.Lifecycle;
010 import com.croftsoft.core.math.MathConstants;
011 import com.croftsoft.core.net.http.HttpLib;
012 import com.croftsoft.core.net.http.msg.HttpMessageClient;
013 import com.croftsoft.core.security.Authentication;
014 import com.croftsoft.core.util.consumer.Consumer;
015 import com.croftsoft.core.util.consumer.NullConsumer;
016 import com.croftsoft.core.util.consumer.QueueConsumer;
017 import com.croftsoft.core.util.loop.Loopable;
018 import com.croftsoft.core.util.loop.Looper;
019 import com.croftsoft.core.util.queue.ListQueue;
020 import com.croftsoft.core.util.queue.Queue;
021 import com.croftsoft.core.util.queue.QueuePuller;
022
023 import com.croftsoft.apps.chat.ChatConstants;
024 import com.croftsoft.apps.chat.event.CreateModelEvent;
025 import com.croftsoft.apps.chat.event.Event;
026 import com.croftsoft.apps.chat.event.MoveEvent;
027 import com.croftsoft.apps.chat.event.NullEvent;
028 import com.croftsoft.apps.chat.event.RemoveModelEvent;
029 import com.croftsoft.apps.chat.event.TalkEvent;
030 import com.croftsoft.apps.chat.request.CreateModelRequest;
031 import com.croftsoft.apps.chat.request.PullRequest;
032 import com.croftsoft.apps.chat.request.ViewRequest;
033 import com.croftsoft.apps.chat.response.CreateModelResponse;
034 import com.croftsoft.apps.chat.response.CreateUserResponse;
035 import com.croftsoft.apps.chat.response.UnknownUserResponse;
036 import com.croftsoft.apps.chat.response.ViewResponse;
037 import com.croftsoft.apps.chat.server.ChatServer;
038
039 /*********************************************************************
040 * Chat client.
041 *
042 * @version
043 * 2003-08-16
044 * @since
045 * 2003-06-10
046 * @author
047 * <a href="http://www.CroftSoft.com/">David Wallace Croft</a>
048 *********************************************************************/
049
050 public final class ChatClient
051 implements Lifecycle
052 //////////////////////////////////////////////////////////////////////
053 //////////////////////////////////////////////////////////////////////
054 {
055
056 private static final boolean DEBUG = true;
057
058 //////////////////////////////////////////////////////////////////////
059 // network constants
060 //////////////////////////////////////////////////////////////////////
061
062 private static final String USER_AGENT
063 = "Chat/1.0";
064
065 private static final long POLLING_PERIOD_MIN
066 = 0;
067
068 private static final long POLLING_PERIOD_MAX
069 = MathConstants.MILLISECONDS_PER_DAY;
070
071 private static final long POLLING_PERIOD_INIT
072 = POLLING_PERIOD_MIN;
073
074 private static final double POLLING_PERIOD_MULT
075 = 2.0;
076
077 private static final double POLLING_PERIOD_DIVI
078 = Double.POSITIVE_INFINITY;
079
080 private static final long POLLING_PERIOD_INCR
081 = MathConstants.MILLISECONDS_PER_SECOND;
082
083 //////////////////////////////////////////////////////////////////////
084 //////////////////////////////////////////////////////////////////////
085
086 private final Authentication authentication;
087
088 private final PullRequest pullRequest;
089
090 private final Queue eventQueue;
091
092 private final Queue requestQueue;
093
094 private final ChatServer chatServer;
095
096 private final Looper looper;
097
098 private final HttpMessageClient httpMessageClient;
099
100 private final Map classToConsumerMap;
101
102 private final QueuePuller queuePuller;
103
104 //
105
106 private long eventIndex;
107
108 //////////////////////////////////////////////////////////////////////
109 //////////////////////////////////////////////////////////////////////
110
111 public ChatClient (
112 Authentication authentication,
113 URL servletURL )
114 //////////////////////////////////////////////////////////////////////
115 {
116 NullArgumentException.check (
117 this.authentication = authentication );
118
119 pullRequest = new PullRequest ( authentication );
120
121 if ( servletURL == null )
122 {
123 requestQueue = new ListQueue ( );
124
125 chatServer = new ChatServer ( );
126
127 queuePuller = new QueuePuller (
128 requestQueue,
129 new Consumer ( )
130 {
131 public void consume ( Object o )
132 {
133 ChatClient.this.consume ( chatServer.serve ( o ) );
134 }
135 } );
136
137 looper = new Looper (
138 new Loopable ( )
139 {
140 public boolean loop ( )
141 {
142 return ChatClient.this.loop ( );
143 }
144 } );
145
146 httpMessageClient = null;
147 }
148 else
149 {
150 httpMessageClient = new HttpMessageClient (
151 servletURL,
152 USER_AGENT,
153 SerializableCoder.INSTANCE,
154 SerializableCoder.INSTANCE,
155 HttpLib.APPLICATION_OCTET_STREAM, // contentType
156 createRequestBytes ( pullRequest ),
157 new Consumer ( )
158 {
159 public void consume ( Object o )
160 {
161 ChatClient.this.consume ( o );
162 }
163 },
164 POLLING_PERIOD_MIN,
165 POLLING_PERIOD_MAX,
166 POLLING_PERIOD_INIT,
167 POLLING_PERIOD_MULT,
168 POLLING_PERIOD_DIVI,
169 POLLING_PERIOD_INCR );
170
171 requestQueue = httpMessageClient.getOutgoingQueue ( );
172
173 chatServer = null;
174
175 looper = null;
176
177 queuePuller = null;
178 }
179
180 eventQueue = new ListQueue ( );
181
182 Consumer eventConsumer = new QueueConsumer ( eventQueue );
183
184 classToConsumerMap = new HashMap ( );
185
186 classToConsumerMap.put (
187 UnknownUserResponse.class,
188 NullConsumer.INSTANCE );
189
190 classToConsumerMap.put (
191 CreateModelResponse.class,
192 NullConsumer.INSTANCE );
193
194 classToConsumerMap.put (
195 CreateUserResponse.class,
196 new CreateUserConsumer (
197 requestQueue,
198 authentication,
199 ChatConstants.DEFAULT_AVATAR_TYPE,
200 ChatConstants.DEFAULT_AVATAR_X,
201 ChatConstants.DEFAULT_AVATAR_Y ) );
202
203 classToConsumerMap.put (
204 CreateModelEvent.class,
205 eventConsumer );
206
207 classToConsumerMap.put (
208 MoveEvent.class,
209 eventConsumer );
210
211 classToConsumerMap.put (
212 NullEvent.class,
213 NullConsumer.INSTANCE );
214
215 classToConsumerMap.put (
216 RemoveModelEvent.class,
217 eventConsumer );
218
219 classToConsumerMap.put (
220 TalkEvent.class,
221 eventConsumer );
222
223 classToConsumerMap.put (
224 ViewResponse.class,
225 eventConsumer );
226
227 eventIndex = new Random ( ).nextLong ( );
228 }
229
230 //////////////////////////////////////////////////////////////////////
231 //////////////////////////////////////////////////////////////////////
232
233 public void init ( )
234 //////////////////////////////////////////////////////////////////////
235 {
236 if ( chatServer != null )
237 {
238 chatServer.init ( );
239
240 looper.init ( );
241
242 queuePuller.init ( );
243 }
244 else
245 {
246 httpMessageClient.init ( );
247 }
248 }
249
250 public void start ( )
251 //////////////////////////////////////////////////////////////////////
252 {
253 if ( chatServer != null )
254 {
255 looper.start ( );
256
257 queuePuller.start ( );
258 }
259 else
260 {
261 httpMessageClient.start ( );
262 }
263 }
264
265 public void stop ( )
266 //////////////////////////////////////////////////////////////////////
267 {
268 if ( chatServer != null )
269 {
270 looper.stop ( );
271
272 queuePuller.stop ( );
273 }
274 else
275 {
276 httpMessageClient.stop ( );
277 }
278 }
279
280 public void destroy ( )
281 //////////////////////////////////////////////////////////////////////
282 {
283 if ( chatServer != null )
284 {
285 chatServer.destroy ( );
286
287 looper.destroy ( );
288
289 queuePuller.destroy ( );
290 }
291 else
292 {
293 httpMessageClient.destroy ( );
294 }
295 }
296
297 //////////////////////////////////////////////////////////////////////
298 //////////////////////////////////////////////////////////////////////
299
300 public Queue getEventQueue ( ) { return eventQueue; }
301
302 public Queue getRequestQueue ( ) { return requestQueue; }
303
304 //////////////////////////////////////////////////////////////////////
305 //////////////////////////////////////////////////////////////////////
306
307 private boolean loop ( )
308 //////////////////////////////////////////////////////////////////////
309 {
310 Object response = chatServer.serve ( pullRequest );
311
312 if ( response != null )
313 {
314 consume ( response );
315 }
316
317 return true;
318 }
319
320 private void consume ( Object o )
321 //////////////////////////////////////////////////////////////////////
322 {
323 if ( DEBUG )
324 {
325 System.out.println ( "ChatClient.consume(" + o + ")" );
326 }
327
328 if ( o instanceof Event )
329 {
330 long eventIndex = ( ( Event ) o ).getEventIndex ( );
331
332 if ( eventIndex == this.eventIndex )
333 {
334 return;
335 }
336
337 if ( eventIndex == this.eventIndex + 1 )
338 {
339 this.eventIndex = eventIndex;
340 }
341 else
342 {
343 requestQueue.replace ( new ViewRequest ( authentication ) );
344 }
345 }
346 else if ( o instanceof ViewResponse )
347 {
348 this.eventIndex = ( ( ViewResponse ) o ).getEventIndex ( );
349 }
350
351 Consumer consumer
352 = ( Consumer ) classToConsumerMap.get ( o.getClass ( ) );
353
354 if ( consumer == null )
355 {
356 System.out.println ( "ChatClient: unknown message: " + o );
357
358 System.out.println ( "Suspending ChatClient..." );
359
360 stop ( );
361 }
362 else
363 {
364 try
365 {
366 consumer.consume ( o );
367 }
368 catch ( Exception ex )
369 {
370 ex.printStackTrace ( );
371
372 System.out.println ( "Suspending ChatClient..." );
373
374 stop ( );
375 }
376 }
377 }
378
379 //////////////////////////////////////////////////////////////////////
380 // private methods
381 //////////////////////////////////////////////////////////////////////
382
383 private byte [ ] createRequestBytes ( Serializable serializable )
384 //////////////////////////////////////////////////////////////////////
385 {
386 try
387 {
388 return SerializableCoder.INSTANCE.encode ( serializable );
389 }
390 catch ( Exception ex )
391 {
392 // this should never happen if constants are OK
393
394 ex.printStackTrace ( );
395
396 throw
397 ( RuntimeException ) new RuntimeException ( ).initCause ( ex );
398 }
399 }
400 /*
401 private void processMoveEvent ( MoveEvent moveEvent )
402 //////////////////////////////////////////////////////////////////////
403 {
404 MoveEvent moveEvent = ( MoveEvent ) o;
405
406 ChatModel chatModel
407 = chatWorld.getChatModel ( moveEvent.getModelId ( ) );
408
409 if ( chatModel == null )
410 {
411 return;
412 }
413
414 PointXY origin = moveEvent.getOrigin ( );
415
416 if ( origin != null )
417 {
418 chatModel.setCenter ( origin.getX ( ), origin.getY ( ) );
419 }
420
421 chatModel.setDestination ( moveEvent.getDestination ( ) );
422
423 return;
424 }
425 }
426
427 private void processUserUnknownResponse (
428 UserUnknownResponse userUnknownResponse )
429 //////////////////////////////////////////////////////////////////////
430 {
431 requestQueue.replace (
432 new CreateUserRequest ( authentication ) );
433 }
434 */
435
436 //////////////////////////////////////////////////////////////////////
437 //////////////////////////////////////////////////////////////////////
438 }