true
if the login succeeds.
*/
private boolean login(String username)
{
synchronized (_chatUsers)
{
if (!_chatUsers.contains(username))
{
return _chatUsers.add(username);
}
else
{
return false;
}
}
}
/**
* Logout the user from the system. If the user cannot be found then
* a false
value is returned here.
*
* @param username The user to logout
* @return Value is true
if the logout succeeds.
*/
private boolean logout(String username)
{
synchronized (_chatUsers)
{
if (_chatUsers.contains(username))
{
return _chatUsers.remove(username);
}
else
{
return false;
}
}
}
/**
* Send a message to either a user or a room. If there is an error the
* an IOException occurs.
*
* @param user The user originating the message.
* @param dest The destination, either another user or a room.
* @param msg Text of the message to dispatch.
* @throws IOException Fatal error.
*/
private void msg(String user, String dest, String msg) throws IOException, ChatServerException
{
synchronized (_chatHandles)
{
// the # indicates we are sending msg to room
if ('#' == dest.charAt(0))
{
StringBuffer outMsg = new StringBuffer();
outMsg.append(user).append(' ');
outMsg.append(dest).append(' ');
outMsg.append(msg);
for (int idx = 0; idx < _chatHandles.size(); idx++)
{
ChatServerHandler handle = (ChatServerHandler)_chatHandles.get(idx);
if (handle.hasRoom(dest))
{
handle.send(ChatResponse.GOTROOMMSG, outMsg.toString());
}
}
return;
}
else
{
StringBuffer outMsg = new StringBuffer();
outMsg.append(user).append(' ');
outMsg.append(msg);
for (int idx = 0; idx < _chatHandles.size(); idx++)
{
ChatServerHandler handle = (ChatServerHandler)_chatHandles.get(idx);
if (handle.isUser(dest))
{
handle.send(ChatResponse.GOTUSERMSG, outMsg.toString());
return;
}
}
throw new ChatServerException("Specified User Not Found: " + dest);
}
}
}
/**
* Inner class to process the commands from a connection that has been
* accepted from the main loop.
*
* @author eric
*/
class ChatServerHandler implements Runnable
{
private Socket _sock = null;
private InetAddress _addr = null;
private String _username = null;
private Vector _chatRooms = new Vector();
OutputStreamWriter _writer = null;
ChatServerHandler(Socket sock)
{
_sock = sock;
_addr = _sock.getInetAddress();
}
public void run()
{
try
{
InputStream input = _sock.getInputStream();
OutputStream output = _sock.getOutputStream();
InputStreamReader reader = new InputStreamReader(input);
_writer = new OutputStreamWriter(output);
ByteArrayOutputStream buf = new ByteArrayOutputStream(MAX_BUF_LEN);
int c = -1;
long ts = System.currentTimeMillis(); // track time we started
// some intro...
send(ChatResponse.OK, ChatServer.APP_ABOUT);
while (_sock.isConnected() && (c = reader.read()) != -1)
{
if (MAX_BUF_LEN == buf.size())
{
buf.reset();
// check for command - after max wait the client is killed
if ((System.currentTimeMillis() - ts) > MAX_TIME_WAIT)
{
_sock.close();
}
}
// append chars other that CR
if ((char)c != '\r')
{
buf.write((char)c);
}
// detect newline
if ((char)c == '\n')
{
try
{
processRequest(buf.toString());
send(ChatResponse.OK, null);
}
catch (ChatServerException cse)
{
send(ChatResponse.ERR, cse.getMessage());
}
buf.reset();
ts = System.currentTimeMillis();
}
}
} catch (IOException io)
{
System.err.println("ChatServerListener: " + io.getMessage() + " for host " + _addr.getHostAddress());
}
}
/**
* Response output to the client. Each message to the client must be
* of a specified resp
type.
*
* @param resp The type of message being sent.
* @param text The text of the message to send or null
* if there is no text to send.
* @throws IOException Fatal error during messaging.
*/
protected synchronized void send(ChatResponse resp, String text) throws IOException
{
if (_writer != null)
{
_writer.write(resp.toString());
_writer.write(' ');
if (text != null)
_writer.write(text);
_writer.write("\r\n");
_writer.flush();
}
}
/**
* Ask this server connection if it is for the specified login.
*
* @param username The user to check against.
* @return true
if this connection is for that user.
*/
public boolean isUser(String username)
{
return (_username != null && _username.equals(username));
}
/**
* Indicate if the connection is actively in the specified room.
*
* @param room The room to check.
* @return true
if the user on this connection is in that room.
*/
public boolean hasRoom(String room)
{
boolean hasroom = false;
synchronized (_chatRooms)
{
hasroom = _chatRooms.contains(room);
}
return hasroom;
}
/**
* Parse the command from the user and perform request actions. If
* the command cannot be parsed then return an error.
*
* @param msg The raw data to process. This should be prefixed by
* a valid command with parameters, if the data is
* malformed then an error occurs here.
* @throws ChatServerException Error during processing, most likely
* the result of a malformed command.
*/
private void processRequest(String msg) throws ChatServerException
{
String[] msgarray = msg.trim().split(" ");
ChatCommand cmd = null;
// ignore empty commands
if (0 == msgarray.length)
{
throw new ChatServerException("Invalid Message for Processing");
}
// attempt to parse the command
try
{
cmd = ChatCommand.valueOf(msgarray[0]);
} catch (IllegalArgumentException iae)
{
throw new ChatServerException("Invalid Command (" + msgarray[0] + ")");
}
if (1 == msgarray.length)
{
if (cmd == ChatCommand.LOGOUT)
{
if (_username != null)
{
logout(_username);
_username = null;
}
try
{
_sock.close();
}
catch (IOException io)
{
System.err.println(io.getMessage());
}
return;
}
}
if (2 == msgarray.length)
{
if (cmd == ChatCommand.LOGIN)
{
if (login(msgarray[1]))
{
_username = msgarray[1];
return;
}
else
{
throw new ChatServerException("Username not accepted");
}
}
if (cmd == ChatCommand.JOIN)
{
join(msgarray[1]);
return;
}
if (cmd == ChatCommand.PART)
{
part(msgarray[1]);
return;
}
}
if (msgarray.length > 2)
{
if (cmd == ChatCommand.MSG)
{
StringBuffer messageText = new StringBuffer();
for (int idx = 2; idx < msgarray.length; idx++)
{
messageText.append(msgarray[idx]).append(' ');
}
try
{
msg(_username, msgarray[1], messageText.toString());
return;
} catch (IOException io)
{
throw new ChatServerException(io.getMessage());
}
}
}
// if we got this far - then this is an error
throw new ChatServerException("Specified Command Invalid");
}
/**
* Add the current user to the specified discussion room.
*
* @param group The name of the room to join, it must begin with a #
character.
* @throws ChatServerException Unable to join the specified group.
*/
private void join(String room) throws ChatServerException
{
if ('#' == room.charAt(0))
{
synchronized (_chatRooms)
{
if (_chatRooms.contains(room))
{
throw new ChatServerException("Already joined to " + room);
}
_chatRooms.add(room);
}
}
else
{
throw new ChatServerException("Join Name Must Begin with \'#\' Character");
}
}
/**
* Remove the current user to the specified discussion room.
*
* @param group The name of the room to leave, it must begin with a #
character.
* @throws ChatServerException Unable to leave the specified group.
*/
private void part(String room) throws ChatServerException
{
synchronized (_chatRooms)
{
if (_chatRooms.contains(room))
{
_chatRooms.remove(room);
}
else
{
throw new ChatServerException("Not Joined to " + room);
}
}
}
}
}
./chatserver/src/com/esarjeant/chat/ChatServerShutdown.java 0000644 0000764 0000144 00000001043 10362466626 023144 0 ustar eric users package com.esarjeant.chat;
import java.util.Date;
/**
* Execute server shutdown. Provide feedback to connected clients that
* the server is going offline.
*
* @author $Author $
* @version $Revision $
*/
class ChatServerShutdown implements Runnable
{
private ChatServerListener _listener = null;
protected ChatServerShutdown(ChatServerListener csl)
{
_listener = csl;
}
public void run()
{
System.out.println("Execute Server Shutdown at " + new Date());
synchronized (_listener)
{
_listener.shutdown();
}
}
} ./chatserver/src/com/esarjeant/chat/ChatServerException.java 0000644 0000764 0000144 00000000427 10362456772 023275 0 ustar eric users package com.esarjeant.chat;
/**
* Primitive error handler for the chat server. This is the only exception
* that the server can throw.
*
* @author eric
*/
public class ChatServerException extends Exception
{
public ChatServerException(String msg)
{
super(msg);
}
}
./chatserver/src/com/esarjeant/chat/CVS/ 0000755 0000764 0000144 00000000000 10362476734 017135 5 ustar eric users ./chatserver/src/com/esarjeant/chat/CVS/Root 0000644 0000764 0000144 00000000034 10362466354 017775 0 ustar eric users :pserver:eric@cvs:/home/cvs
./chatserver/src/com/esarjeant/chat/CVS/Repository 0000644 0000764 0000144 00000000042 10362466354 021230 0 ustar eric users chatserver/src/com/esarjeant/chat
./chatserver/src/com/esarjeant/chat/CVS/Entries 0000644 0000764 0000144 00000000520 10362476734 020466 0 ustar eric users /ChatCommand.java/1.1/Sun Jan 15 16:45:58 2006/-kk/
/ChatResponse.java/1.1/Sun Jan 15 16:45:54 2006/-kk/
/ChatServer.java/1.5/Sun Jan 15 16:47:25 2006/-ko/
/ChatServerException.java/1.1/Sun Jan 15 14:36:42 2006/-ko/
/ChatServerListener.java/1.3/Sun Jan 15 16:45:45 2006/-ko/
/ChatServerShutdown.java/1.2/Sun Jan 15 15:43:18 2006/-kk/
D
./chatserver/src/com/esarjeant/chat/ChatServer.java 0000644 0000764 0000144 00000002547 10362476235 021417 0 ustar eric users package com.esarjeant.chat;
import java.io.IOException;
/**
* Primary entry point for the chat server. This starts the main
* listener to begin receiving requests.
*
* @author $Author$
* @version $Revision$
*/
public class ChatServer
{
private static final String APP_NAME = "chatserver";
private static final String APP_VERSION = "0.1";
private static final String APP_BUILD = "011506";
/**
* Product version information for display during startup and connect.
*/
public static final String APP_ABOUT = APP_NAME + " " + APP_VERSION + " (Build " + APP_BUILD + ")";
/**
* Static main entry for the application. There are some command line
* parameters that can be used here:
* -port [number]
*
*
* @param args Refer to the optional arguments specified above.
*/
public static void main(String[] args) throws IOException
{
int port = 8888;
if (2 == args.length)
{
if (args[0].equals("-port"))
{
port = Integer.parseInt(args[1]);
}
}
// start server listener
ChatServerListener csl = new ChatServerListener(port);
Thread thmain = new Thread(csl);
// thread to cleanly shutdown server
ChatServerShutdown css = new ChatServerShutdown(csl);
Runtime.getRuntime().addShutdownHook(new Thread(css));
// begin running...
thmain.start();
}
}
./chatserver/src/com/esarjeant/chat/ChatCommand.java 0000644 0000764 0000144 00000001463 10362476106 021520 0 ustar eric users package com.esarjeant.chat;
/**
* Command set for the chat server. This enumerator describes the available set
* of commands for chat.
*
* @author $Author$
* @version $Revision$
*/
enum ChatCommand {
LOGIN ("LOGIN"), // begin a connection with your name
// LOGIN OK
* and ERROR
respectively.
*
* @author $Author$
* @version $Revision$
*/
enum ChatResponse
{
OK ("OK"), // successful response
ERR ("ERROR"), // error response
GOTROOMMSG ("GOTROOMMSG"), // message for specified room
GOTUSERMSG ("GOTUSERMSG"); // message for specified user
private String _response = null;
ChatResponse(String resp)
{
_response = resp;
}
public String toString()
{
return _response;
}
}
./chatserver/src/com/esarjeant/CVS/ 0000755 0000764 0000144 00000000000 10362457643 016214 5 ustar eric users ./chatserver/src/com/esarjeant/CVS/Root 0000644 0000764 0000144 00000000034 10362466354 017056 0 ustar eric users :pserver:eric@cvs:/home/cvs
./chatserver/src/com/esarjeant/CVS/Repository 0000644 0000764 0000144 00000000035 10362466354 020313 0 ustar eric users chatserver/src/com/esarjeant
./chatserver/src/com/esarjeant/CVS/Entries 0000644 0000764 0000144 00000000013 10362466354 017541 0 ustar eric users D/chat////
./chatserver/src/com/CVS/ 0000755 0000764 0000144 00000000000 10362457643 014240 5 ustar eric users ./chatserver/src/com/CVS/Root 0000644 0000764 0000144 00000000034 10362466354 015102 0 ustar eric users :pserver:eric@cvs:/home/cvs
./chatserver/src/com/CVS/Repository 0000644 0000764 0000144 00000000023 10362466354 016334 0 ustar eric users chatserver/src/com
./chatserver/src/com/CVS/Entries 0000644 0000764 0000144 00000000020 10362466354 015563 0 ustar eric users D/esarjeant////
./chatserver/src/CVS/ 0000755 0000764 0000144 00000000000 10362457643 013462 5 ustar eric users ./chatserver/src/CVS/Root 0000644 0000764 0000144 00000000034 10362466354 014324 0 ustar eric users :pserver:eric@cvs:/home/cvs
./chatserver/src/CVS/Repository 0000644 0000764 0000144 00000000017 10362466354 015561 0 ustar eric users chatserver/src
./chatserver/src/CVS/Entries 0000644 0000764 0000144 00000000012 10362466354 015006 0 ustar eric users D/com////
./chatserver/CVS/ 0000755 0000764 0000144 00000000000 10362477346 012675 5 ustar eric users ./chatserver/CVS/Root 0000644 0000764 0000144 00000000034 10362457552 013535 0 ustar eric users :pserver:eric@cvs:/home/cvs
./chatserver/CVS/Repository 0000644 0000764 0000144 00000000013 10362457552 014766 0 ustar eric users chatserver
./chatserver/CVS/Entries 0000644 0000764 0000144 00000000300 10362477346 014222 0 ustar eric users D/src////
/MANIFEST.MF/1.1/Sun Jan 15 13:48:44 2006/-ko/
/.classpath/1.1/Sun Jan 15 15:37:24 2006/-kk/
/.project/1.1/Sun Jan 15 15:37:24 2006/-kk/
/build.xml/1.4/Sun Jan 15 16:55:28 2006/-ko/
./chatserver/build/ 0000755 0000764 0000144 00000000000 10362501044 013320 5 ustar eric users ./chatserver/build/com/ 0000755 0000764 0000144 00000000000 10362501044 014076 5 ustar eric users ./chatserver/build/com/esarjeant/ 0000755 0000764 0000144 00000000000 10362501044 016052 5 ustar eric users ./chatserver/build/com/esarjeant/chat/ 0000755 0000764 0000144 00000000000 10362501044 016771 5 ustar eric users ./chatserver/build/com/esarjeant/chat/ChatCommand.class 0000644 0000764 0000144 00000002571 10362501044 022203 0 ustar eric users 1 E 3
4 5 6
7
8 9
: ; < = > ? @ LOGIN Lcom/esarjeant/chat/ChatCommand; LOGOUT JOIN PART MSG _command Ljava/lang/String; $VALUES ![Lcom/esarjeant/chat/ChatCommand; values #()[Lcom/esarjeant/chat/ChatCommand; Code LineNumberTable valueOf 4(Ljava/lang/String;)Lcom/esarjeant/chat/ChatCommand; LocalVariableTable name