I'm looking for some hints or advice regarding efficiency, performance and some good coding practices. Also I'm curious about synchronization. The server is multithreaded, so I think some operations should be synchronized.
HttpServer.java
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class HttpServer {
public static void main(String args[]) {
int serverPort = 0;
try (InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("properties.xml")) {
Properties prop = new Properties();
prop.loadFromXML(is);
serverPort = Integer.parseInt(prop.getProperty("serverPort"));
}
catch (IOException e) {
e.printStackTrace();
}
ServerListener server = new ServerListener(serverPort);
server.startServer();
}
}
ServerListener.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;
class ServerListener implements Runnable {
Thread t;
ServerSocket serverSocket;
HashSet<ServerThread> threadList;
LinkedList<Message> lastMessages;
TreeSet<String> userList;
int serverPort;
ServerListener(int serverPort) {
t = new Thread(this);
threadList = new HashSet<>();
lastMessages = new LinkedList<>();
userList = new TreeSet<>();
this.serverPort = serverPort;
}
public void run() {
try {
serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(serverPort));
System.out.println("Listening on " + serverSocket.getInetAddress() + ":" + serverSocket.getLocalPort());
while (true) {
Socket socket = serverSocket.accept();
System.out.println("Client connected (" + socket.getRemoteSocketAddress() + ")");
threadList.add(new ServerThread(socket, threadList, lastMessages, userList));
}
}
catch (SocketException e) {
System.out.println("SocketException " + e);
}
catch (IOException e) {
System.out.println("IOException " + e);
}
finally {
try {
serverSocket.close();
}
catch (IOException e) {
System.out.println("IOException " + e);
}
}
}
void startServer() {
t.start();
}
void stopServer() throws IOException {
serverSocket.close();
}
}
ServerThread.java
import java.io.*;
import java.net.Socket;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Properties;
import java.util.TreeSet;
class ServerThread implements Runnable {
private Thread t;
private Socket socket;
private HashSet<ServerThread> threadList;
private LinkedList<Message> lastMessages;
private ObjectOutputStream out;
private Message message;
private Authentication auth;
private int maximumMessages;
private TreeSet<String> userList;
ServerThread(Socket socket, HashSet<ServerThread> threadList, LinkedList<Message> lastMessages, TreeSet<String> userList) {
this.socket = socket;
this.threadList = threadList;
this.lastMessages = lastMessages;
this.userList = userList;
t = new Thread(this);
t.start();
try (InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("properties.xml")) {
Properties prop = new Properties();
prop.loadFromXML(is);
maximumMessages = Integer.parseInt(prop.getProperty("maximumLastMessages"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
out = new ObjectOutputStream(socket.getOutputStream());
}
catch (IOException e) {
System.out.println("IOException: " + e);
}
try (ObjectInputStream messageObject = new ObjectInputStream(socket.getInputStream())) {
auth = (Authentication) messageObject.readObject();
if (checkUser(auth)) {
out.writeObject(new Message("Server", "Connected"));
userList.add(auth.getUserName());
for (Message msg : lastMessages) sendMessage(msg);
while (true) {
try {
message = (Message) messageObject.readObject();
}
catch (EOFException e) {
break;
}
lastMessages.addLast(message);
if (lastMessages.size() > maximumMessages) lastMessages.removeFirst();
for (ServerThread thread : threadList) thread.sendMessage(message);
}
}
else {
out.writeObject(new Message("Server", "Authentication failed"));
System.out.println("Access denied (" + socket.getRemoteSocketAddress() + ")");
}
}
catch (ClassNotFoundException e) {
System.out.println("Class not found " + e);
}
catch (IOException e) {
System.out.println("IOException: " + e);
}
finally {
try {
out.close();
}
catch (IOException e) {
System.out.println("Cannot close connection " + e);
}
threadList.remove(this);
userList.remove(auth.getUserName());
System.out.println("Client disconnected (" + socket.getRemoteSocketAddress() + ")");
}
}
private void sendMessage(Message msg) {
try {
out.writeObject(msg);
}
catch (NullPointerException e) {
System.out.println("Cannot send message. Message is empty " + e);
} catch (IOException e) {
System.out.println("Cannot send message " + e);
}
}
private boolean checkUser(Authentication auth) {
boolean result = false;
try (InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("userlist.xml")) {
Properties prop = new Properties();
prop.loadFromXML(is);
if (auth.getUserName().equals("Server") || userList.contains(auth.getUserName())) {
result = false;
}
else result = prop.getProperty(auth.getUserName()).equals(auth.getUserPassword());
}
catch (NullPointerException e) {
System.out.println("User not found");
return false;
}
catch (IOException e) {
System.out.println("Cannot open user list " + e);
}
return result;
}
}
Message.java
import java.io.Serializable;
import java.util.Date;
class Message implements Serializable {
private String userName;
private String message;
private Date date;
Message(String userName, String message) {
this.userName = userName;
this.message = message;
date = new Date();
}
public String toString() {
if (userName.equals("Server")) {
return message;
}
else return date + " [" + userName + "] " + message;
}
}
Authentication.java
import java.io.Serializable;
public class Authentication implements Serializable {
private String userName;
private String userPassword;
Authentication(String userName, String userPassword) {
this.userName = userName;
this.userPassword = userPassword;
}
String getUserName() {
return userName;
}
String getUserPassword() {
return userPassword;
}
}
HttpClient.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class HttpClient {
public static void main(String args[]) throws IOException {
String message;
String userName;
String userPassword;
BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter your name: ");
userName = bReader.readLine();
System.out.print("Enter your password: ");
userPassword = bReader.readLine();
try (Socket clientSocket = new Socket("192.168.1.123", 777)) {
new ClientReceiver(clientSocket);
try (ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream())) {
Authentication auth = new Authentication(userName, userPassword);
out.writeObject(auth);
while ((message = bReader.readLine()) != null) {
Message messageObject = new Message(userName, message);
out.writeObject(messageObject);
}
}
}
}
}
ClientReceiver.java
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.SocketException;
class ClientReceiver implements Runnable {
private Thread t;
private Socket clientSocket;
private Message message;
ClientReceiver(Socket clientSocket) {
t = new Thread(this);
t.start();
this.clientSocket = clientSocket;
}
public void run() {
try (ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream())) {
while (true) {
try {
message = (Message) in.readObject();
}
catch (EOFException e) {
break;
}
System.out.println(message);
}
}
catch (SocketException e) {
System.out.println("Disconnected");
}
catch (ClassNotFoundException e) {
System.out.println("Class not found " + e);
}
catch (IOException e) {
System.out.println("IOException: " + e);
}
}
}