diff --git a/JChatServer/.classpath b/JChatServer/.classpath
new file mode 100644
index 0000000..fb565a5
--- /dev/null
+++ b/JChatServer/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/JChatServer/.project b/JChatServer/.project
new file mode 100644
index 0000000..28dac34
--- /dev/null
+++ b/JChatServer/.project
@@ -0,0 +1,17 @@
+
+
+ JChatServer
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/JChatServer/.settings/org.eclipse.jdt.core.prefs b/JChatServer/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/JChatServer/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/JChatServer/bin/pad/prac2/ChatException.class b/JChatServer/bin/pad/prac2/ChatException.class
new file mode 100644
index 0000000..0e0d11e
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/ChatException.class
diff --git a/JChatServer/bin/pad/prac2/Connection.class b/JChatServer/bin/pad/prac2/Connection.class
new file mode 100644
index 0000000..53b85cd
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/Connection.class
diff --git a/JChatServer/bin/pad/prac2/JChat$1.class b/JChatServer/bin/pad/prac2/JChat$1.class
new file mode 100644
index 0000000..e8fef6f
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/JChat$1.class
diff --git a/JChatServer/bin/pad/prac2/JChat.class b/JChatServer/bin/pad/prac2/JChat.class
new file mode 100644
index 0000000..5c8923a
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/JChat.class
diff --git a/JChatServer/bin/pad/prac2/MyServerSocket.class b/JChatServer/bin/pad/prac2/MyServerSocket.class
new file mode 100644
index 0000000..97d4974
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/MyServerSocket.class
diff --git a/JChatServer/bin/pad/prac2/MySocket.class b/JChatServer/bin/pad/prac2/MySocket.class
new file mode 100644
index 0000000..ea93697
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/MySocket.class
diff --git a/JChatServer/bin/pad/prac2/Server.class b/JChatServer/bin/pad/prac2/Server.class
new file mode 100644
index 0000000..a6de29a
--- /dev/null
+++ b/JChatServer/bin/pad/prac2/Server.class
diff --git a/JChatServer/src/pad/prac2/ChatException.java b/JChatServer/src/pad/prac2/ChatException.java
new file mode 100644
index 0000000..ab9017e
--- /dev/null
+++ b/JChatServer/src/pad/prac2/ChatException.java
@@ -0,0 +1,21 @@
+package pad.prac2;
+
+public class ChatException extends Exception
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 2263415822589627234L;
+
+ public ChatException()
+ {
+ super();
+ }
+
+ public ChatException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/JChatServer/src/pad/prac2/Connection.java b/JChatServer/src/pad/prac2/Connection.java
new file mode 100644
index 0000000..3d8c263
--- /dev/null
+++ b/JChatServer/src/pad/prac2/Connection.java
@@ -0,0 +1,153 @@
+package pad.prac2;
+
+import java.io.IOException;
+
+public class Connection extends Thread
+{
+ private MySocket socket;
+ private Server serv;
+ private boolean sleep, kill;
+
+ public Connection(Server s)
+ {
+ serv = s;
+ kill = false;
+ sleep = true;
+ }
+
+ public void awake()
+ {
+ synchronized(this)
+ {
+ sleep = false;
+ this.notify();
+ }
+ }
+
+ public void finish()
+ {
+ kill = true;
+ if(sleep)
+ {
+ sleep = false;
+ notify();
+ }
+ socket.close();
+ }
+
+ public void setSock(MySocket s)
+ {
+ socket = s;
+ }
+
+ public void sendMessage(String message) throws IOException
+ {
+ socket.sendMsg(message);
+ }
+
+ public void run()
+ {
+ while(!kill)
+ {
+ while(sleep)
+ {
+ try
+ {
+ synchronized(this)
+ {
+ wait();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ if(!kill)
+ {
+ String str;
+ try
+ {
+ str = socket.recvMsg();
+ while(str.contains(" "))
+ {
+ socket.sendMsg("CHATNICKINVALID");
+ str = socket.recvMsg();
+ }
+ while(serv.isOnline(str))
+ {
+ socket.sendMsg("CHATNICKEXIST");
+ str = socket.recvMsg();
+ }
+ socket.sendMsg("CHATOK");
+ serv.addToChatroom(this, str);
+ }
+ catch (IOException e)
+ {
+ if(!kill)
+ {
+ System.out.println("TCP: Communication with client failed while entering chatroom");
+ sleep = true;
+ serv.finishWorker(this);
+ }
+ continue;
+ }
+ while(true)
+ {
+ try
+ {
+ str = "/disconnect";
+ str = socket.recvMsg();
+ if(str.equals("/disconnect"))
+ {
+ socket.sendMsg("DISC_OK");
+ System.out.println(serv.getNickname(this) + " disconnected");
+ break;
+ }
+ else if(str.equals("/exit"))
+ {
+ socket.sendMsg("EXIT_OK");
+ System.out.println(serv.getNickname(this) + " disconnected");
+ break;
+ }
+ else if(str.equals("/who"))
+ {
+ socket.sendMsg(serv.listOnline());
+ }
+ else if(str.startsWith("@"))
+ {
+ try
+ {
+ if(!str.contains(" "))
+ {
+ serv.sendTo(this,str.substring(1),"");
+ }
+ else
+ {
+ serv.sendTo(this,str.substring(1,str.indexOf(' ')),str.substring(str.indexOf(' ')+1));
+ }
+ }
+ catch(ChatException cE)
+ {
+ socket.sendMsg(cE.getMessage());
+ }
+ }
+ else
+ {
+ System.out.println("FROM " + serv.getNickname(this) + ": " + str);
+ serv.sendToChat(this,str);
+ }
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error writing to socket");
+ System.out.println(serv.getNickname(this) + " disconnected");
+ break;
+ }
+ }
+ sleep = true;
+ }
+ serv.finishWorker(this);
+ }
+ }
+}
diff --git a/JChatServer/src/pad/prac2/JChat.java b/JChatServer/src/pad/prac2/JChat.java
new file mode 100644
index 0000000..a906273
--- /dev/null
+++ b/JChatServer/src/pad/prac2/JChat.java
@@ -0,0 +1,30 @@
+package pad.prac2;
+
+import java.util.Scanner;
+
+public class JChat
+{
+ public static void main(String[] args)
+ {
+ String ip;
+ int port, roomSize;
+ Scanner in = new Scanner(System.in);
+ System.out.print("IP: ");
+ ip = in.nextLine();
+ System.out.print("Port: ");
+ port = in.nextInt();
+ System.out.print("Size of chatroom: ");
+ roomSize = in.nextInt();
+ in.close();
+ final Server serv = new Server(ip,port,roomSize);
+ Runtime.getRuntime().addShutdownHook(new Thread()
+ {
+ public void run()
+ {
+ System.out.println("JChat: Caught interrupt, killing server...");
+ serv.killServer();
+ }
+ });
+ serv.startServer();
+ }
+}
diff --git a/JChatServer/src/pad/prac2/MyServerSocket.java b/JChatServer/src/pad/prac2/MyServerSocket.java
new file mode 100644
index 0000000..ae9a6e3
--- /dev/null
+++ b/JChatServer/src/pad/prac2/MyServerSocket.java
@@ -0,0 +1,47 @@
+package pad.prac2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+
+public class MyServerSocket extends ServerSocket
+{
+ public MyServerSocket() throws IOException
+ {
+ super();
+ }
+
+ public void bind(String ip, int port)
+ {
+ SocketAddress addr = new InetSocketAddress(ip,port);
+ try
+ {
+ super.bind(addr, 5);
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error binding socket to address");
+ }
+ }
+
+ public MySocket accept() throws IOException
+ {
+ MySocket incoming = new MySocket();
+ super.implAccept(incoming);
+ incoming.initializeStreams();
+ return incoming;
+ }
+
+ public void close()
+ {
+ try
+ {
+ super.close();
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error while closing socket");
+ }
+ }
+}
diff --git a/JChatServer/src/pad/prac2/MySocket.java b/JChatServer/src/pad/prac2/MySocket.java
new file mode 100644
index 0000000..26d367b
--- /dev/null
+++ b/JChatServer/src/pad/prac2/MySocket.java
@@ -0,0 +1,118 @@
+package pad.prac2;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+
+public class MySocket extends Socket
+{
+ private BufferedReader input;
+ private OutputStream output;
+
+ public MySocket()
+ {
+ super();
+ }
+
+ public void connect(String host, int port)
+ {
+ SocketAddress addr = new InetSocketAddress(host,port);
+ try
+ {
+ super.connect(addr);
+ initializeStreams();
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error occured while connecting to remote host");
+ }
+ }
+
+ public void initializeStreams()
+ {
+ try
+ {
+ output = super.getOutputStream();
+ input = new BufferedReader(new InputStreamReader(super.getInputStream()));
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error initializing socket");
+ }
+ }
+
+ public void sendMsg(String msg) throws IOException
+ {
+ String length = new Integer(msg.length()).toString();
+ this.write(length + '\0' + msg);
+ }
+
+ public String recvMsg() throws IOException
+ {
+ String len,str;
+ int length;
+ len = this.readLine();
+ if(len == "")
+ {
+ return len;
+ }
+ length = Integer.parseInt(len);
+ str = this.read(length);
+ return str;
+ }
+
+ public void write(String str) throws IOException
+ {
+ output.write(str.getBytes());
+ }
+
+ public String read(int bytes)
+ {
+ char[] buffer = new char[bytes];
+ try
+ {
+ input.read(buffer, 0, bytes);
+ return new String(buffer);
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error retrieving data from remote endpoint");
+ return null;
+ }
+ }
+
+ public String readLine() throws IOException
+ {
+
+ String line = "";
+ char[] c = new char[1];
+ while(true)
+ {
+ input.read(c,0,1);
+ if(c[0] == '\0')
+ {
+ break;
+ }
+ line += c[0];
+ }
+ return line;
+ }
+
+ public void close()
+ {
+ try
+ {
+ super.shutdownInput();
+ super.shutdownOutput();
+ super.close();
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error while closing socket");
+ }
+ }
+}
\ No newline at end of file
diff --git a/JChatServer/src/pad/prac2/Server.java b/JChatServer/src/pad/prac2/Server.java
new file mode 100644
index 0000000..4d860de
--- /dev/null
+++ b/JChatServer/src/pad/prac2/Server.java
@@ -0,0 +1,196 @@
+package pad.prac2;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class Server
+{
+ private MyServerSocket ss;
+ private int roomSize;
+ private boolean kill;
+ private Connection[] workerPool;
+ private int freeWorkers;
+ private Lock lock;
+ private Condition availableWorkers;
+ private ConcurrentHashMap activeConnections;
+
+ public Server(String ip, int port, int rS)
+ {
+ try
+ {
+ ss = new MyServerSocket();
+ lock = new ReentrantLock();
+ activeConnections = new ConcurrentHashMap();
+ roomSize = rS;
+ freeWorkers = roomSize;
+ kill = false;
+ availableWorkers = lock.newCondition();
+ workerPool = new Connection[roomSize];
+ for(int i = 0; i < roomSize; i++)
+ {
+ workerPool[i] = new Connection(this);
+ workerPool[i].start();
+ }
+ ss.bind(ip, port);
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error initializating server socket");
+ }
+ }
+
+ public void startServer()
+ {
+ while(!kill)
+ {
+ try
+ {
+ MySocket incoming = ss.accept();
+ startWorker(incoming);
+ }
+ catch(IOException ioExc)
+ {
+ if(!kill)
+ {
+ System.out.println("TCP: Error accepting connection");
+ break;
+ }
+ }
+ }
+
+ }
+
+ public void startWorker(MySocket s)
+ {
+ lock.lock();
+ while(freeWorkers == 0)
+ {
+ try
+ {
+ s.sendMsg("CHATFULL");
+ s.close();
+ lock.unlock();
+ return;
+ }
+ catch(IOException ioExc)
+ {
+ System.out.println("TCP: Error while sending message to client");
+ }
+ }
+ workerPool[roomSize - freeWorkers].setSock(s);
+ workerPool[roomSize - freeWorkers].awake();
+ workerPool[roomSize - freeWorkers] = null;
+ freeWorkers--;
+ lock.unlock();
+ }
+
+ public void finishWorker(Connection c)
+ {
+ lock.lock();
+ removeFromChatroom(c);
+ freeWorkers++;
+ workerPool[roomSize - freeWorkers] = c;
+ availableWorkers.signal();
+ lock.unlock();
+ }
+
+ public void sendTo(Connection orig, String nick, String msg) throws IOException, ChatException
+ {
+ lock.lock();
+ System.out.println("FROM " + getNickname(orig) + " TO " + nick + ": " + msg);
+ msg = getNickname(orig) + ": " + msg;
+ Set conns = activeConnections.keySet();
+ Iterator it = conns.iterator();
+ Connection dest;
+ while(it.hasNext())
+ {
+ dest = it.next();
+ if(getNickname(dest).equals(nick))
+ {
+ dest.sendMessage(msg);
+ lock.unlock();
+ return;
+ }
+ }
+ lock.unlock();
+ throw new ChatException("No such nickname");
+ }
+
+ public void sendToChat(Connection origin, String message) throws IOException
+ {
+ lock.lock();
+ String nickname = activeConnections.get(origin);
+ Set connections = activeConnections.keySet();
+ Iterator it = connections.iterator();
+ Connection conn;
+ while(it.hasNext())
+ {
+ conn = it.next();
+ if(conn != origin)
+ {
+ conn.sendMessage(nickname + ": " + message);
+ }
+ }
+ lock.unlock();
+ }
+
+ public String getNickname(Connection c)
+ {
+ return activeConnections.get(c);
+ }
+
+ public void addToChatroom(Connection c, String nickName)
+ {
+ activeConnections.put(c, nickName);
+ System.out.println(nickName + " has entered the room");
+ }
+
+ private void removeFromChatroom(Connection c)
+ {
+ activeConnections.remove(c);
+ }
+
+ public boolean isOnline(String nick)
+ {
+ return activeConnections.contains(nick);
+ }
+
+ public String listOnline()
+ {
+ lock.lock();
+ String ret = new Integer(activeConnections.size()).toString();
+ ret += " people currently online:";
+ Collection nickNames = activeConnections.values();
+ Iterator it = nickNames.iterator();
+ while(it.hasNext())
+ {
+ ret += "\n";
+ ret += it.next();
+ }
+ lock.unlock();
+ return ret;
+ }
+
+ public void killServer()
+ {
+ kill = true;
+ ss.close();
+ finishConnections();
+ }
+
+ public void finishConnections()
+ {
+ Set conns = activeConnections.keySet();
+ Iterator it = conns.iterator();
+ while(it.hasNext())
+ {
+ it.next().finish();
+ }
+ }
+}