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         }