/** ChatServerSubject object
-- implementation of the Subject class, containing the user list, an observer list, and the message buffer
-- This class will notify each child thread of the server whenever a change is made on either of these 2 objects.
Authors: Jon Fidler & Daniel Tanner
March 31st, 2010
CMPT 352 - Networking
*/
import java.util.*;
import java.net.*;
import java.awt.*;
public class ChatServerSubject extends Observable
{
//members
private Vector<String> userList;
private Vector<MessageObject> messageBuffer;
private String removedUser;
//flags
/* whatHappened flag = int representing the state of the object.
0 = nothing's changed
1 = user added
2 = user removed
3 = message added
*/
private int whatHappened;
//constructors
public ChatServerSubject()
{
userList = new Vector<String>();
messageBuffer = new Vector<MessageObject>();
removedUser = "NOBODY";
whatHappened = 0;
}
/** userList changing functions
*/
//add a user
public void addUser(String userName)
{
userList.add(userName);
//set flags
whatHappened = 1;
setChanged();
notifyObservers();
}
//remove a user from the list
public void removeUser(String userName)
{
removedUser = userName;
userList.remove(userName);
//set flags
whatHappened = 2;
setChanged();
notifyObservers();
}
//check if a user is on the list or not
public boolean userExists(String userName)
{
return userList.contains(userName);
}
//get user list
public Vector<String> getUserList(){return userList;}
//get the name of the user who just disconnected
public String getDCedUser() {return removedUser;}
//returns the last user on the list (most recently added user
public String getNewUser() {return userList.lastElement();}
/**Message Buffer functions
*/
//get message function
public MessageObject getMessage() {return messageBuffer.firstElement();}
//add message function
public void addMessage(MessageObject message)
{
messageBuffer.add(message);
whatHappened = 3;
setChanged();
notifyObservers();
}
//remove a message
public void removeMessage(MessageObject message)
{
messageBuffer.remove(message);
}
/* Flag checking functions*/
public boolean userAdded()
{
if(whatHappened == 1)
return true;
return false;
}
public boolean userRemoved()
{
if(whatHappened == 2)
return true;
return false;
}
public boolean messageAdded()
{
if(whatHappened == 3)
return true;
return false;
}
}
/**
Daniel Tanner & Jon Fidler
CMPT 352 - Computer Networking
March 29th, 2010
Handler class for threaded Chat Server
*/
import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;
import java.util.regex.Pattern;
public class Handler implements Observer
{
//Create variables
private BufferedReader fromClient = null;
private BufferedOutputStream toClient = null;
private ChatServerSubject buffer = null;
private String myUserName = null;
private String command = null;
private boolean connectedProperly = false;
private static Pattern alphaNum = Pattern.compile("^[A-Za-z0-9\\*]+$");
//This method invoked as a separate thread
public void process(Socket client, ChatServerSubject buffer) throws IOException
{
//set up the method
this.buffer = buffer;
//try block for opening streams and getting hostname
try
{
//open Streams
fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
toClient = new BufferedOutputStream(client.getOutputStream());
//initial connection state
command = fromClient.readLine();
command = command.toLowerCase();
if(!command.equalsIgnoreCase("connect"))
{
error("Bad Request. Please input a proper command.", 2);
return;
}
//get username, check against userList
myUserName = fromClient.readLine();
myUserName = myUserName.toUpperCase();
if(buffer.userExists(myUserName))
{
error("Duplicate Handle. Please try another name.", 1);
return;
}
//check that username is within bounds
if(!(myUserName.length() <= 16 && alphaNum.matcher(myUserName).matches()))
{
error("Invalid handle. Handle must be an alphanumeric string with size less than or euqal to 16.", 3);
return;
}
connectedProperly = true;
//add username to userList
buffer.addUser(myUserName);
buffer.addObserver(this);
//print userList to client
printUserList();
//listen for commands
do
{
command = fromClient.readLine();
command = command.toUpperCase();
//getHandles command
if(command.equalsIgnoreCase("gethandles"))
printUserList();
//Post command
else if(command.equalsIgnoreCase("post"))
constructMessage();
//invalid command
else if(!command.equalsIgnoreCase("disconnect"))
error("Invalid request. Please input a proper command.", 2);
}
while(!command.equalsIgnoreCase("disconnect"));
}
catch(IOException ioe)
{
System.err.println(ioe);
return;
}
finally
{
//disconnection state
if(connectedProperly)
{
buffer.deleteObserver(this);
buffer.removeUser(myUserName);
}
// close streams and socket
if (fromClient != null)
fromClient.close();
if (toClient != null)
toClient.close();
if (client != null)
client.close();
}
}
//broadcast method - this method is the basic broadcasting method
//it assumes that we're sending a public message
private void broadcast(MessageObject message) throws IOException
{
System.out.println("You've reached the broadcast method!");
toClient.write("posted\n".getBytes());
toClient.write((message.getFromUser() + "\n").getBytes());
toClient.write("public\n".getBytes());
toClient.write((message.getMessage() + "\n").getBytes());
toClient.flush();
}
//private message broadcast method
private void privateMessage(MessageObject message) throws IOException
{
System.out.println("privateMessage method!");
toClient.write("posted\n".getBytes());
toClient.write((message.getFromUser() + "\n").getBytes());
toClient.write("private\n".getBytes());
toClient.write((message.getMessage() + "/n").getBytes());
toClient.flush();
}
//connected/disconnected message
private void userConnected(String userName, boolean connected) throws IOException
{
if(connected)
toClient.write("connected\n".getBytes());
else
toClient.write("disconnected\n".getBytes());
toClient.write((userName + "\n").getBytes());
toClient.flush();
}
//Error message
private void error(String errorMessage, int errorNum) throws IOException
{
toClient.write("error\n".getBytes());
toClient.write((errorNum + "\n").getBytes());
toClient.write((errorMessage + "\n").getBytes());
toClient.flush();
}
//Print user list function
private void printUserList() throws IOException
{
Vector<String> currentUserList = new Vector<String>(buffer.getUserList());
toClient.write("handles\n".getBytes());
toClient.write((currentUserList.size() + "\n").getBytes());
for(int index = 0; index < currentUserList.size(); index++)
toClient.write((currentUserList.get(index) + "\n").getBytes());
toClient.flush();
}
//CreateMessage method - goes to this state after receiving the post command from the user
private void constructMessage() throws IOException
{
MessageObject newMessage = new MessageObject();
newMessage.setFromUser(myUserName);
String temp;
//read the toUser
temp = fromClient.readLine();
temp = temp.toUpperCase();
if(!(alphaNum.matcher(temp).matches() && temp.length() <= 16))
{
error("Invalid username entered. Try again!.", 3);
return;
}
if(!buffer.userExists(temp) && !temp.equalsIgnoreCase("*"))
{
error("User not found.", 4);
return;
}
newMessage.setToUser(temp);
//read the message and check to make sure it's within bounds
temp = fromClient.readLine();
if(temp.length() > 1024)
{
error("Message too Long", 5);
return;
}
newMessage.setMessage(temp);
buffer.addMessage(newMessage);
}
//update function - this is the observer functionanility implemented
//it will check the flags when the subject has changed, and act appropriately
public void update(Observable o, Object arg)
{
System.out.println("your observers are being updated!");
try
{
//check if a user was added or removed
if(buffer.userAdded())
userConnected(buffer.getNewUser(), buffer.userAdded());
else if(buffer.userRemoved())
userConnected(buffer.getDCedUser(), buffer.userAdded());
//here's the routine when we get a new message
else if(buffer.messageAdded())
{
MessageObject newMessage = buffer.getMessage();
//check if it's a private message
if(newMessage.getToUser().equals(myUserName))
privateMessage(newMessage);
//now check if it's a broadcast and not from me
else if(newMessage.getToUser().equals("*") && !newMessage.getFromUser().equals(myUserName))
broadcast(newMessage);
//now decrement and check if the counter is at 0, remove from the list if it is
newMessage.viewed();
if(newMessage.getCounter() == 0)
buffer.removeMessage(newMessage);
}
}
catch(IOException ioe)
{
System.err.println(ioe);
command = "disconnect";
return;
}
}
}
/** Connection Class for threaded Chat Server
Daniel Tanner & John Fidler
CMPT 352 - Computer Networking
March 29th, 2010
*/
import java.net.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class Connection implements Runnable
{
//create variables
private Socket client;
private ChatServerSubject buffer;
private Handler handler = new Handler();
public Connection(Socket client, ChatServerSubject buffer)
{
this.client = client;
this.buffer = buffer;
}
//Separate thread
public void run()
{
try
{
handler.process(client, buffer);
}
catch (java.io.IOException ioe)
{
System.err.println(ioe);
}
}
}
/** Chat Server
Authors: Daniel J. Tanner and Jon Fidler
March 29th, 2010
CMPT 352 - Networking
This program implements the Spring 2010 Networking class' Chat Protocol
**/
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer
{
//constants & globals
public static final int PORT = 2222;
private static ChatServerSubject buffer = null;
public static void main(String[] args) throws java.io.IOException
{
//load everything up
buffer = new ChatServerSubject();
//Create socket
ServerSocket sock = null;
try
{
//establish socket
sock = new ServerSocket(PORT);
while(true)
{
Socket client = sock.accept();
//debug testing
InetAddress ipAddr = client.getInetAddress();
System.out.print(ipAddr.getHostAddress() + " : ");
System.out.println(client.getPort() + " connected.");
Thread worker = new Thread(new Connection(client, buffer));
worker.start();
}
}
//catch any errors opening sockets
catch(IOException ioe)
{
System.err.println(ioe);
System.exit(0);
}
finally
{
//close streams
if(sock != null)
sock.close();
}
}
//getUserList function - returns a pointer to the user List
public static Vector<String> getUserList() {return buffer.getUserList();}
}
/**
BufferKeyEntry class
Contain fields to put into the key of the message buffer
The value will be the unformatted message
Members of the BufferEntry are as follows:
-Who sent the message (so we don't echo it back)
-A non-static counter which will tell us when all the threads have sent the message
so we know when to remove the message from our buffer.
-Who the message is to (* for broadcasts)
*/
/**
Authors: Daniel Tanner & Jon Fidler
March 29th, 2010
CMPT 352 - Networking
*/
import java.net.*;
import java.io.*;
import java.text.*;
import java.util.*;
public class MessageObject
{
//create variables
private String toUser;
private String fromUser;
private int counter;
private String message;
//constructor
MessageObject()
{
counter = ChatServer.getUserList().size();
toUser = null;
fromUser = null;
message = null;
}
//copy constructor
MessageObject(MessageObject copy)
{
this.toUser = copy.toUser;
this.fromUser = copy.fromUser;
this.counter = copy.counter;
this.message = copy.message;
}
//Constructor with arguments
MessageObject(String toUser, String fromUser, String message)
{
counter = ChatServer.getUserList().size();
this.toUser = toUser;
this.fromUser = fromUser;
this.message = message;
}
//get functions
int getCounter() {return counter;}
String getToUser() {return toUser;}
String getFromUser() {return fromUser;}
String getMessage() {return message;}
//set functions
void setToUser(String toUser) {this.toUser = toUser;}
void setFromUser(String fromUser) {this.fromUser = fromUser;}
void setMessage(String message) {this.message = message;}
//viewed function - This function will decrement the counter
void viewed() {counter--;}
//equals function
boolean equals(MessageObject rhs)
{
if(toUser.equals(rhs.toUser) && fromUser.equals(rhs.fromUser) && counter == rhs.counter && message.equals(rhs.message))
return true;
else
return false;
}
}
/**
Daniel Tanner & Jon Fidler
CMPT 352 - Computer Networking
March 29th, 2010
Handler class for threaded Chat Server
*/
import java.net.*;
import java.lang.Object.*;
import java.util.*;
import java.text.*;
import javax.activation.*;
import java.io.*;
import java.util.regex.Pattern;
public class Handler implements Observer
{
//Create variables
private BufferedReader fromClient = null;
private BufferedOutputStream toClient = null;
private ChatServerSubject buffer = null;
private String myUserName = null;
private String command = null;
private boolean connectedProperly = false;
private static Pattern alphaNum = Pattern.compile("^[A-Za-z0-9\\*]+$");
//This method invoked as a separate thread
public void process(Socket client, ChatServerSubject buffer) throws IOException
{
//set up the method
this.buffer = buffer;
//try block for opening streams and getting hostname
try
{
//open Streams
fromClient = new BufferedReader(new InputStreamReader(client.getInputStream()));
toClient = new BufferedOutputStream(client.getOutputStream());
//initial connection state
command = fromClient.readLine();
command = command.toLowerCase();
if(!command.equalsIgnoreCase("connect"))
{
error("Bad Request. Please input a proper command.", 2);
return;
}
//get username, check against userList
myUserName = fromClient.readLine();
myUserName = myUserName.toUpperCase();
if(buffer.userExists(myUserName))
{
error("Duplicate Handle. Please try another name.", 1);
return;
}
//check that username is within bounds
if(!(myUserName.length() <= 16 && alphaNum.matcher(myUserName).matches()))
{
error("Invalid handle. Handle must be an alphanumeric string with size less than or euqal to 16.", 3);
return;
}
connectedProperly = true;
//add username to userList
buffer.addUser(myUserName);
buffer.addObserver(this);
//print userList to client
printUserList();
//listen for commands
do
{
command = fromClient.readLine();
command = command.toUpperCase();
//getHandles command
if(command.equalsIgnoreCase("gethandles"))
printUserList();
//Post command
else if(command.equalsIgnoreCase("post"))
constructMessage();
//invalid command
else if(!command.equalsIgnoreCase("disconnect"))
error("Invalid request. Please input a proper command.", 2);
}
while(!command.equalsIgnoreCase("disconnect"));
}
catch(IOException ioe)
{
System.err.println(ioe);
return;
}
finally
{
//disconnection state
if(connectedProperly)
{
buffer.deleteObserver(this);
buffer.removeUser(myUserName);
}
// close streams and socket
if (fromClient != null)
fromClient.close();
if (toClient != null)
toClient.close();
if (client != null)
client.close();
}
}
//broadcast method - this method is the basic broadcasting method
//it assumes that we're sending a public message
private void broadcast(MessageObject message) throws IOException
{
System.out.println("You've reached the broadcast method!");
toClient.write("posted\n".getBytes());
toClient.write((message.getFromUser() + "\n").getBytes());
toClient.write("public\n".getBytes());
toClient.write((message.getMessage() + "\n").getBytes());
toClient.flush();
}
//private message broadcast method
private void privateMessage(MessageObject message) throws IOException
{
System.out.println("privateMessage method!");
toClient.write("posted\n".getBytes());
toClient.write((message.getFromUser() + "\n").getBytes());
toClient.write("private\n".getBytes());
toClient.write((message.getMessage() + "/n").getBytes());
toClient.flush();
}
//connected/disconnected message
private void userConnected(String userName, boolean connected) throws IOException
{
if(connected)
toClient.write("connected\n".getBytes());
else
toClient.write("disconnected\n".getBytes());
toClient.write((userName + "\n").getBytes());
toClient.flush();
}
//Error message
private void error(String errorMessage, int errorNum) throws IOException
{
toClient.write("error\n".getBytes());
toClient.write((errorNum + "\n").getBytes());
toClient.write((errorMessage + "\n").getBytes());
toClient.flush();
}
//Print user list function
private void printUserList() throws IOException
{
Vector<String> currentUserList = new Vector<String>(buffer.getUserList());
toClient.write("handles\n".getBytes());
toClient.write((currentUserList.size() + "\n").getBytes());
for(int index = 0; index < currentUserList.size(); index++)
toClient.write((currentUserList.get(index) + "\n").getBytes());
toClient.flush();
}
//CreateMessage method - goes to this state after receiving the post command from the user
private void constructMessage() throws IOException
{
MessageObject newMessage = new MessageObject();
newMessage.setFromUser(myUserName);
String temp;
//read the toUser
temp = fromClient.readLine();
temp = temp.toUpperCase();
if(!(alphaNum.matcher(temp).matches() && temp.length() <= 16))
{
error("Invalid username entered. Try again!.", 3);
return;
}
if(!buffer.userExists(temp) && !temp.equalsIgnoreCase("*"))
{
error("User not found.", 4);
return;
}
newMessage.setToUser(temp);
//read the message and check to make sure it's within bounds
temp = fromClient.readLine();
if(temp.length() > 1024)
{
error("Message too Long", 5);
return;
}
newMessage.setMessage(temp);
buffer.addMessage(newMessage);
}
//update function - this is the observer functionanility implemented
//it will check the flags when the subject has changed, and act appropriately
public void update(Observable o, Object arg)
{
System.out.println("your observers are being updated!");
try
{
//check if a user was added or removed
if(buffer.userAdded())
userConnected(buffer.getNewUser(), buffer.userAdded());
else if(buffer.userRemoved())
userConnected(buffer.getDCedUser(), buffer.userAdded());
//here's the routine when we get a new message
else if(buffer.messageAdded())
{
MessageObject newMessage = buffer.getMessage();
//check if it's a private message
if(newMessage.getToUser().equals(myUserName))
privateMessage(newMessage);
//now check if it's a broadcast and not from me
else if(newMessage.getToUser().equals("*") && !newMessage.getFromUser().equals(myUserName))
broadcast(newMessage);
//now decrement and check if the counter is at 0, remove from the list if it is
newMessage.viewed();
if(newMessage.getCounter() == 0)
buffer.removeMessage(newMessage);
}
}
catch(IOException ioe)
{
System.err.println(ioe);
command = "disconnect";
return;
}
}
}
Friday Night Open Thread: Motivation
2 hours ago