Lesson 15 - Java Chat - Client - Introducing the Application Structure
In previous lessons, we created a basic Java server, which we completed in the lesson Java Server - Plugin System Improvements.
Starting with today's lesson, we'll be creating the promised chat as a sample application using our universal Java server.
Architecture
We'll write the client in Java and the JavaFX framework. We'll stick to the
MVC
architecture to keep the code readable. We'll write all views as separate
.fxml
files. The logic will be in the model and everything will be
wired via the controller.
Features
The chat will include the most basic communication features:
- sending a message
- notification that the user started to write a message
We won't implement adding friends to contacts here. Every user will be able to write to everyone. There will also be no registration / login process. When connecting to the server, the user will only enters their name/nickname which is how they'll appear in the chat. If you like the course, we'll also implement end2end encrypted messaging.
Client Structure
To save us some time, I created a basic structure of the client app to which we're going to add our code. Please download the archive attached at the end of this lesson. It contains the client structure (and the code from today's lesson in case something goes wrong for you).
The archive contains:
- code from previous lessons on creating the server
- the skeleton of the client in the
client
module
The client
module has the following file structure:
The controller
package contains classes representing controllers
for each functionality. MainController
will contain the logic of
the main window which is displayed at the application start.
ConnectController
will manage the window in which we'll connect to
the server. The model
package will contain all the models we're
going to create and use during the lessons. The service
package
will include classes for advanced functionality, such as
ChatService
, which will include methods for sending messages.
The ChatApp
class initializes the JavaFX application. We've
already created the LanServerFinder
class and we'll use it in the
future to find servers in the local network. The resources/
folder
contains additional files necessary to run the application. For now, there are
only .fxml
files that serve as views for each controller.
Introducing the App Structure
If we run the application with the command
./gradlew :client:jfxRun
, we'll see the basic application
window.
This window is defined in the main.fxml
file and its controller
is in the MainController
class.
The window consists of three parts:
- menu: contains only buttons to connect to the server and exit the application
- on the left there's a listView, which will show the connected users
- on the right is a chat window with separated tabs for each user
Displaying Local Servers
The second separate window that exists in the application takes care of
connecting/disconnecting to/from the server. In this window, servers from the
local network will be displayed. The window the user will see is defined in the
connect.fxml
file. The controller of this window is in the
ConnectController
class. The image of the resulting window is shown
below:
The window is divided into three parts again:
- at the top there's information about the current server and a disconnect button
- at the bottom, there're text boxes for the server address, port and nickname that we'll use; at the bottom right is a button to connect to the specified server
- all servers found in the local network will be displayed in the middle
There are three controllers in the controller
package. We've
already seen two of them so logically the last one should be
ChatController
. This controller won't have its own window, but will
show user conversation and a text box to create a new message with a send button
in the main window.
Threadpool
Before we begin implementing specific chat client logic, we'll create a
ThreadPool
class to run background tasks easily. We'll place the
class in the root package next to the client's main class:
package cz.stechy.chat; public final class ThreadPool { public static final ExecutorService COMMON_EXECUTOR = ForkJoinPool.commonPool(); public static final Executor JAVAFX_EXECUTOR = Platform::runLater; public static final ScheduledExecutorService SCHEDULER = Executors.newSingleThreadScheduledExecutor(); static void shutDown() { COMMON_EXECUTOR.shutdown(); SCHEDULER.shutdown(); try { COMMON_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } try { SCHEDULER.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } } private ThreadPool() { throw new AssertionError(); } }
There are three public constants in the class:
COMMON_EXECUTOR
- A general threadpool for standard operationsJAVAFX_EXECUTOR
- An executor for executing code in the main threadSCHEDULER
- An executor for running a task later
The shutdown()
method terminates the activities of individual
executors safely. We'll call this method when closing the main application
window. We'll call the shutdown()
methods on individual executors
to start the terminating process. If, for some reason, the termination isn't
done within five seconds, they'll be killed using the
awaitTermination()
method. In the ChatApp
class where
the main window is created, we need to add a listener on closing the window:
stage.setOnCloseRequest(windowEvent -> ThreadPool.shutDown());
By this we should be familiar with the structure of the client application.
Next, in the lesson Java Chat - Client - View Local Servers, we'll implement displaying servers found in the local network and connecting to the them.