• Main Page
  • Related Pages
  • Classes
  • Files
  • File List

ChatServer.java

Go to the documentation of this file.
00001 
00002 import java.io.IOException;
00003 import java.net.InetAddress;
00004 import java.net.ServerSocket;
00005 import java.net.Socket;
00006 import java.net.UnknownHostException;
00007 import java.util.ArrayList;
00008 
00009 /**
00010  *  Chat server principal class that listens on socket for new connections.
00011  *  It instantiates a new threaded client back-end for each new connection and keeps all
00012  *  such client connections in the list. Client back-end thread purges itself from the
00013  *  list after remote end is disconnected.
00014  *  
00015  *  @author Mikica B Kocic
00016  */
00017 public class ChatServer extends Thread {
00018 
00019     /**
00020      *  Provides message presentation context to instance of <code>ChatServer</code>.
00021      */
00022     public interface Context
00023     {
00024         /**
00025          *  Logs message to log area
00026          *  
00027          *  @param str    message that will be logged
00028          */
00029         public abstract void logMessage( String str );
00030 
00031         /**
00032          *  Updates status message.
00033          *  
00034          *  @param str    new status message
00035          */
00036         public abstract void logStatus( String str );
00037     }
00038 
00039     //////////////////////////////////////////////////////////////////////////////////////
00040 
00041     /**
00042      *  Server identifier; concatenated host name with TCP port
00043      */
00044     private String serverID;
00045     
00046     /**
00047      *  TCP port that chat server listens for new connections 
00048      */
00049     private int port;
00050 
00051     /**
00052      *  Collection of active threaded chat clients (instances of 
00053      *  <code>ChatServerClient</code>).  
00054      */
00055     private ArrayList<ChatServerClient> clients;
00056     
00057     /**
00058      *  Indicates if thread is (or should be) running
00059      */
00060     private volatile boolean running;
00061     
00062     /**
00063      *  Event (call-back) context for the instance
00064      */
00065     private Context context;
00066     
00067     /**
00068      *  Creates instance of the chat server.
00069      *  
00070      *  @param port     TCP port to listen
00071      *  @param context  context where to log messages and send status updates
00072      */
00073     public ChatServer( int port, Context context )
00074     {
00075         this.clients  = new ArrayList<ChatServerClient> ();
00076         this.port     = port;
00077         this.context  = context;
00078         this.running  = true;
00079         this.serverID = "";
00080     }
00081 
00082     /**
00083      *  Get number of active client back-ends
00084      */
00085     public int getClientCount ()
00086     {
00087         synchronized( clients )
00088         {
00089             return clients.size ();
00090         }
00091     }
00092 
00093     /**
00094      *  Broadcasts message (terminated with new line) to all clients
00095      *  
00096      *  @param from    originator, if <code>null</code> means that system sends a message
00097      *  @param message textual message that should be broadcasted
00098      */
00099     public void broadcast( ChatServerClient from, String message )
00100     {
00101         if ( from == null ) { // Message from the system
00102             context.logMessage( message );
00103             message = "[System] :: " + message;
00104         }
00105         else {
00106             context.logMessage( "[" + from.getClientName() + "] " + message );
00107         }
00108         
00109         synchronized( clients )
00110         {
00111             for( ChatServerClient c: clients ) {
00112                 c.send( message );
00113             }
00114         }
00115     }
00116 
00117     /**
00118      *  Responds to "wwhhoo" message. Broadcasts list of connected users
00119      *  to all connected users.
00120      */
00121     public void wwhhoo( ChatServerClient from )
00122     {
00123         context.logMessage( "[" + from.getClientName() + "] wwhhoo" );
00124 
00125         synchronized( clients )
00126         {
00127             for( ChatServerClient c: clients ) {
00128                 String message = "WWHHOO: " + c.getClientName();
00129                 for( ChatServerClient c2: clients ) {
00130                     c2.send( message );
00131                 }
00132             }
00133         }
00134     }
00135 
00136     /**
00137      *  Logs messages from Eliza sent to client
00138      *  
00139      *  @param message textual message that should be broadcasted
00140      *  @param to      client
00141      */
00142     public void logEliza( String message, ChatServerClient to )
00143     {
00144         context.logMessage( "Eliza -> [" + to.getClientName() + "] " + message );
00145     }
00146 
00147     /**
00148      *  Adds new client's back-end to the list of back-ends
00149      */
00150     public void addClient( Socket clientSocket )
00151     {
00152         int n; // will contain number of client after removal, used for status message
00153 
00154         synchronized( clients )
00155         {
00156             ChatServerClient client = new ChatServerClient( this, clientSocket );
00157 
00158             clients.add( client );
00159             client.start ();
00160             n = clients.size ();
00161         }
00162 
00163         /* update context status message appropriately
00164          */
00165         context.logStatus( serverID +
00166                 ( n == 0 ? ", idle" : 
00167                   n == 1 ? ", 1 client active " 
00168                          : ", " + n + " clients active" )
00169               );
00170     }
00171     
00172     /**
00173      *  Removes existing client's back-end from the list of back-ends
00174      */
00175     public void removeClient( ChatServerClient cli )
00176     {
00177         int n; // will contain number of client after removal, used for status message
00178         
00179         synchronized( clients )
00180         {
00181             clients.remove( cli );
00182             n = clients.size ();
00183         }
00184 
00185         /* update context status message appropriately
00186          */
00187         context.logStatus( serverID +
00188                   ( n == 0 ? ", idle" : 
00189                     n == 1 ? ", 1 client active " 
00190                            : ", " + n + " clients active" )
00191                 );
00192     }
00193     
00194     /**
00195      *  Listens on socket for new connections. After accepting new connection,  
00196      *  instantiates a new threaded client back-end for that connection.
00197      */
00198     @Override
00199     public void run ()
00200     {
00201         ServerSocket socket = null;
00202         
00203         /* Create socket
00204          */
00205         try
00206         {
00207             socket = new ServerSocket( this.port );
00208         }
00209         catch( IOException e )
00210         {
00211             context.logMessage( e.toString () );
00212             running = false;
00213         }
00214         
00215         /* Setup server ID, if we have socket...
00216          */
00217         if ( running )
00218         {
00219             try {
00220                 serverID = InetAddress.getLocalHost().getHostName ()
00221                          + ":" + socket.getLocalPort ();
00222             } catch( UnknownHostException e ) {
00223                 serverID = "unknown:" + socket.getLocalPort ();
00224             }
00225             context.logStatus( serverID + ", idle" );
00226             context.logMessage( "Server at " + serverID 
00227                                 + " ready to accept new clients..." );
00228         }
00229 
00230         /* Listen for new connections, accept clients and add them to the
00231          * list of back-ends.
00232          */
00233         while( running )
00234         {
00235             try
00236             {
00237                 addClient( socket.accept () );
00238             }
00239             catch ( IOException e )
00240             {
00241                 context.logMessage( e.toString () );
00242                 running = false;
00243             }
00244         }
00245         
00246         /* Inform parent that we are dead. Note that <code>", dead"</code> will
00247          * trigger also change in color of the log area background.
00248          */
00249         context.logStatus( serverID + ":" + port + ", dead" );
00250         context.logMessage( "Done." );
00251     }
00252 }
00253 

Generated on Thu Dec 16 2010 14:26:32 for Chat Server by  doxygen 1.7.2