Get up to 80 % extra points for free! More info:

Lesson 4 - Java Server - Server Thread

In the previous lesson, Java Server - Google Guice, we put dependency management in the hands of the Google guice library. Today we're going to create a basic code template for our Java server thread.

Thread Features

This thread will serve as the main entrance point. This is where the connection with clients will be established.

We'll create a new core package into which we'll put all the important functionality of the server.

Thread Factory

We'll start as in the previous lesson by creating a factory to instantiate the thread. In the core package, we'll create a new server package in which we'll create the following classes and interfaces:

  • IServerThread - An interface providing methods to communicate with the server thread
  • IServerThreadFactory - An interface with a method to create an IServerThread instance
  • ServerThread - An IServerThread interface implementation
  • ServerThreadFactory - An IServerThreadFactory interface implementation

We'll put just this interface directly into the core package:

  • IThreadControl - A helper interface for working with threads

Let's define methods for the IThreadControl interface first. This interface will contain two methods:

  • start() to start the thread
  • shutdown() to inform the thread that it should start the shutdown sequence

The signature of the methods will be as follows:

void start();
void shutdown();

We'll inherit the IServerThread interface from IThreadControl.

The IServerThreadFactory interface will contain one factory method to create an IServerThread class instance:

IServerThread getServerThread(IParameterProvider parameters) throws IOException;

Implementing the Interfaces

We've defined the interface, so let's implement them. We'll start with the ServerThread class, which implements the IServerThread interface. We'll modify the class first and let it inherit from the Thread class. Thus, the class declaration will look like this:

class ServerThread extends Thread implements IServerThread

We'll add one class constant to the class:

private static final int SOCKET_TIMEOUT = 5000;

one instance constant:

private final int port;

and one instance variable:

private boolean running = false;

which will indicate whether the thread should run or terminate.

Next, we'll create a constructor that accepts a single parameter: int port. In the constructor, we'll set the thread name to "ServerThread" and initialize the port instance constant:

ServerThread(int port) throws IOException {
    super("ServerThread");
    this.port = port;
}

Finally, we'll implement the methods that the interface defines:

@Override
public void shutdown() {
    running = false;
    try {
        join();
    } catch (InterruptedException ignored) {}
}

@Override
public void run() {
    try (ServerSocket serverSocket = new ServerSocket(port)) {
        serverSocket.setSoTimeout(SOCKET_TIMEOUT);
        while (running) {
            try {
                final Socket socket = serverSocket.accept();
            } catch (SocketTimeoutException ignored) {}
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

In the shutdown() method, we set the running variable to false and call join() to wait for the thread to finish. The run() method creates a new ServerSocket class instance and sets its timeout to the value of SOCKET_TIMEOUT, which is 5 seconds. This ensures that every 5 seconds a SocketTimeoutException is thrown and the running variable is checked. Then we call accept() on the ServerSocket instance. This call is blocking, which means that the thread will freeze until the client is connected or an exception is thrown. We'll leave the processing of the newly established connection for the next lesson. This would be all for the ServerThread class for this moment. Let's start implementing the factory.

Implementing the Factory

We'll add the @Singleton annotation from the Google guice library to the ServerThreadFactory class. This annotation ensures that whenever we ask for the factory in the future, we'll get the same instance of it. The ServerThreadFactory class must implement a single getServerThread() method. This method accepts an instance implementing IParameterProvider instance as a parameter to get command line parameters. In the factory, we'll define default values for the parameters that will be used if we don't pass some of the parameters when starting the server:

// Default port
private static final int DEFAULT_SERVER_PORT = 15378;
// Default maximum number of clients
private static final int DEFAULT_MAX_CLIENTS = 3;
// Default waiting queue size
private static final int DEFAULT_WAITING_QUEUE_SIZE = 1;

Now we can fill the getServerThread() method's body:

@Override
public IServerThread getServerThread(IParameterProvider parameters) throws IOException {
    final int port = parameters.getInteger(CmdParser.PORT, DEFAULT_SERVER_PORT);
    final int maxClients = parameters.getInteger(CmdParser.CLIENTS, DEFAULT_MAX_CLIENTS);
    final int waitingQueueSize = parameters.getInteger(CmdParser.MAX_WAITING_QUEUE, DEFAULT_WAITING_QUEUE_SIZE);

    return new ServerThread(port);
}

In the method, we obtain the individual parameters and finally create and return a new ServerThread class instance. In the future we'll use the remaining parameters as well.

After implementing all interfaces in concrete classes, we can register the server thread factory in the ServerModule class. The registration will be the same as with the parameter factory:

bind(IServerThreadFactory.class).to(ServerThreadFactory.class);

Finally, we'll move to the Server class, where we'll wire everything together. First we'll add another instance constant of the IServerThreadFactory type:

private final IServerThreadFactory serverThreadFactory;

and we'll also add the same parameter to the constructor to initialize the factory:

@Inject
public Server(IParameterFactory parameterFactory, IServerThreadFactory serverThreadFactory) {
    this.parameterFactory = parameterFactory;
    this.serverThreadFactory = serverThreadFactory;
}

Now let's modify the run() method:

private void run(String[]args) throws IOException {
    final IParameterProvider parameters = parameterFactory.getParameters(args);
    final IServerThread serverThread = serverThreadFactory.getServerThread(parameters);
    serverThread.start();
    while(true) {
        final String input = scanner.nextLine();
        if ("exit".equals(input)) {
            break;
        }
    }
    serverThread.shutdown();
}

In the method, we first get the parameters and pass them to the server thread factory, which returns an IServerThread class instance. We run the server by the start() method. The thread starts, but because we haven't defined any activity, it terminates immediately. Then there's an infinite loop awaiting user input until the user enters the word "exit". When the user enters "exit", the server will start shutting down. The shutdown() method tells the server thread to start terminating.

That would be all of today's lesson. Next time, in the lesson Java Server - Connection Manager, we'll create a class to handle incoming clients.


 

Previous article
Java Server - Google Guice
All articles in this section
Server for Client Applications in Java
Skip article
(not recommended)
Java Server - Connection Manager
Article has been written for you by Petr Štechmüller
Avatar
User rating:
1 votes
Activities