Releases: 0xGhazy/quick-chat
Mini-Discord v1.0
Mini-Discord is a multi-threaded console Java application that handles multiple client requests and conversations simultaneously. This is the version I have submitted as an assessment to apply as a junior Java developer at @Mokalamat company.
Overview
Mini-Discord is a multi-threaded console Java application that handles multiple client requests and conversations at the same time. it consists of main two parts Client Application
and Server Application
. In the following section, I'll describe how they work internally and show the features with screenshots.
About the server
Overview
The server is the main part of the Mini Discord application. it starts to listen on the specified port and connect with clients to handle communication and requests.
the most important role of the server is handling multiple requests simultaneously, each client has its own Client Handler
which is responsible for sending and receiving messages, and commands from and to the client.
What happens under the hood?
- A new client opens the client from its end, this will inform the server that a new lobby client is active now.
- The server will accept the new client request, then create a new
client handler
and start a new thread for this handler and start it.
// ---- code snippet ----
Socket clientSocket = serverSocket.accept();
// generate a temporary name for lobby clients.
String tempUsername = "lobby-client-" + DateTimeHandler.timestampNow();
logger.logThis("info", "Listening for client requests. . .");
logger.logThis("request", "A new lobby client request is accepted");
ClientHandler clientHandler = new ClientHandler(clientSocket, tempUsername);
Thread thread = new Thread(clientHandler);
thread.start();
// ---- code snippet ----
- By Client handler constructor take the
socket
, andusername
as arguments to initialize the client handler object by setting the username for this, and the most important role here is to addthis
-newly created- to theclientsList
which hold all current active handlers.
class ClientHandler implements Runnable {
/* ---- code snippet ---- */
// shared by all handlers
private static ArrayList<ClientHandler> clientsList = new ArrayList<>();
public ClientHandler(Socket socket, String username) {
try {
this.socket = socket;
this.bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
this.bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.username = username;
clientsList.add(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/* ---- code snippet ---- */
}
-
By starting the client thread, it will trigger the Overrided
run
method from theRunnable
interface. which will handle the incoming client requests by the specified flag in the request message. -
Each client request to be handled by the handler must consist of FLAG, DELIMITER, and PAYLAOD. handler will parse it choose which function will be executed and replay it to the client.
Parsing incoming commands from the client:
// reading commands from the client side.
command = bufferedReader.readLine();
String[] commandArray = command.split(Settings.DELIMITER);
String flag = commandArray[0];
String payload = commandArray[1];
Choose which function will be executed:
if(flag.equals("[RESET]"))
{
logger.logThis("request", "Reset password request is captured");
Credentials credentials = credentialsService.deSerialize(payload);
String username = credentials.getUsername();
String password = credentials.getPassword();
// hashing the new password
password = HashingService.getSha256(password);
String result = databaseAPI.updatePassword(username, password);
logger.logThis("info", "Account updated [" + result + "]");
sendMessage(this, result);
}
Internal flags
The clients send the user message that applies the following structure
FLAG DELIMITER PAYLOAD
Note: No spaces between them, for example of [VALIDATE]
usage.
[VALIDATE]>hossam
| | |
| | (Username)
| (DELIMITER)
(FLAG)
Here is a list of all available and handled flags on the server side with their descriptions.
Flag | Server usage | Followed By |
---|---|---|
[SIGNUP] |
Signup a new user | Serialized User object |
[LOAD] |
Read the user from the database then return it to the client | Username |
[RESET] |
Reset user password | Serialized Credentials object |
[VALIDATE] |
Validate if the username is exist in database | Username |
[UPDATE] |
Update the user words and conversations | Serialized User object |
[SNAPSHOT] |
Ask the server to return conversation | Username |
[AUTH] |
Authorize user in login process | Serialized Credentials object |
[JOIN] |
Add user to the chat room | Username |
[BROADCAST] |
Send message to all peers in chat room | "{sendername}~{message}" |
[LEAVE] |
inform all users a member has left | Username |
Server project directory architecture
Files in | Description |
---|---|
database | The database API responsible for dealing with the database and tasks such as adding new user , update user statistics , etc. |
model | the main object classes that the server and the client exchange together |
server | contains the server backend logic such as running the server , and handling incoming requests |
service | all needed functionality by the models such as serialization , deserialization , and hashing function |
utils | all utility functions such as logging, colored output, banners, etc. |
Settings.java | all settings needed by the server is here such as port , host , database connection , etc. |
Features & Screenshots
- Secured stored credentials
All confidential data is stored hashed in the database using theSHA256
algorithm. fromguava
library
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
public static String getSha256(String plainPassword) throws NoSuchAlgorithmException {
return Hashing.sha256()
.hashString(plainPassword, StandardCharsets.UTF_8)
.toString();
}
- Logging events
I have implemented logging events handler/utils/Logger.java
to enable logging events and verbosity to make it easier to know what is happening right now with each event.
- Settings class
in theSettings.java
you can specify the settings of the server such as the following.
public class Settings {
public static final Integer PORT = 5000;
public static final String DATABASE_USERNAME = "_USERNAME_";
public static final String DATABASE_PASSWORD = "_PASSWORD_";
private static final String DATABASE_NAME = "DATABASE_NAME";
public static final String DATABASE_URI = "jdbc:mysql://localhost:3306/" + DATABASE_NAME;
// this property must be set in the client too
// it's the DELIMITER between flag and payload.
public static final String DELIMITER = ">";
}
About the Client
Overview
The client is the second important part of the application, by running the client
it will try to connect to the server on the specified host and port number in the Settings.java
. seeing the prompt
means you can ask the server for the actions you need in your current mode.
The client has 3 main modes:
Mode | Description | How to active? |
---|---|---|
Lobby | The default mode when opening the client | default |
Active | The mode loaded after login, your profile is loaded here to be ready for dumping | /login |
Chatting | Mode in the chat room, in which you send messages to all peers | /join |
Use /help
to get the current available commands for Lobby
, and Active
modes.
Client project directory architecture
Files in | Description |
---|---|
model | The main object classes that the server and the client exchange together |
network | Connection handlers such as client connection handler , and message listener |
service | All needed functionality by the models such as serialization , deserialization , hashing , reporting functions |
ui | All ui modes a... |