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="https://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 }