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.javaimport 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.javaimport 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.javaimport 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.javaimport 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.javaimport 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.javaimport 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.javaimport 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);        }    }}
TCP Chat (Server/Client)
java;server;tcp;client