Lesson 2 - Java Server - Server Parameters
In the previous lesson, Java Server - Introduction, we prepared the basic structure of the
project. In today's tutorial, we're going to start with the server part,
specifically process its parameters. All the classes we're going to create will
relate to the server
module unless specified otherwise.
Server Parameters
The server will be a simple console application, so we'll have to pass all settings as parameters when we start the program. Over time, we'll add a simple interactive control panel as well.
The parameters will be as follows:
- port number: defines the port on which our server will listen
- number of clients: the maximum number of connected clients that will actively communicate with the server
- waiting queue size: if there's the maximum number of clients on the server, others will be queued
Individual parameters in the command line will be separated by commas. Each
parameter will consist of a -name=value
pair. The resulting format
will be as follows:
-port=6298, -clients=2, -max_waiting_queue=5
Once we have defined what the server parameters will look like, we can
implement them in a class that takes care of parsing parameters from the command
line. We'll create a new package named cmd
and create an
IParameterProvider
interface in it. This interface will define the
methods we'll use to get the parameters:
package cz.stechy.chat.cmd; public interface IParameterProvider { String DEFAULT_STRING = ""; int DEFAULT_INTEGER = -1; default String getString(String key) { return getString(key, DEFAULT_STRING); } String getString(String key, String def); default int getInteger(String key) { return getInteger(key, DEFAULT_INTEGER); } int getInteger(String key, int def); }
We defined a total of 4 methods in the interface. Two to get text and two to get a number. We use the possibility of default methods in the interface so that we don't have to implement all the methods unnecessarily.
Now in the same package we'll create a class that will implement this
interface, we'll name it CmdParser
. Let's define constants
representing names of individual parameters:
public static final String PORT = "port"; public static final String CLIENTS = "clients"; public static final String MAX_WAITING_QUEUE = "max_waiting_queue";
Next we'll define a Map
that will store all the parameters:
private final Map<String, String> map = new HashMap<>();
We'll create a class constructor that accepts input arguments from the command line as a parameter:
public CmdParser(String[] args) { for (String arg : args) { arg = arg.replace("-", ""); String[] raw = arg.split("="); map.put(raw[0], raw[1]); } }
We process the individual parameters in the constructor. First we get rid of
the -
character before each parameter name, then we split the
individual name=value
pairs into an array of two values. Then we
insert these values into the map.
Finally, we need to implement the methods that the
IParameterProvider
interface requires:
@Override public String getString(String key, String def) { final String s = map.get(key); return s == null ? def : s; } @Override public int getInteger(String key, int def) { final String s = map.get(key); return s == null ? def : Integer.parseInt(s); }
The implementation is very simple. A value is obtained from the map that should match the key. If this value doesn't exist, we return the default value, otherwise we return the value that was obtained from the map.
Testing
To verify that our class works as expected, we'll write a simple unit test. To
create tests we have the test/
folder ready. In the folder
test/java/
we'll create the appropriate package, i.e:
cz.stechy.chat.cmd
. Here we'll create a new
ParametersTest
class. First we'll define a constant that will
represent test parameters as they come from the command line:
private static final String[] PARAMETERS = { "-port=6298", "-clients=5", "-max_waiting_queue=5", "name=test" };
Next, we'll create a variable of the IParameterProvider
type:
private IParameterProvider parameterProvider;
In the setUp()
method, we'll initialize this variable:
@Before public void setUp() throws Exception { parameterProvider = new CmdParser(PARAMETERS); }
Now we can test that we've implemented the interface correctly. So we'll create four test methods. Two to get the text and two to get the number:
@Test public void getStringTest() { final String key = "name"; final String name = "test"; assertEquals("Error, server name value mismatch.", name, parameterProvider.getString(key)); } @Test public void getStringNegativeTest() { final String key = "unknown"; final String value = CmdParser.DEFAULT_STRING; assertEquals("Error, wrongly defined default value.", value, parameterProvider.getString(key)); } @Test public void getIntegerTest() { final String key = "port"; final int value = 6298; assertEquals("Error, port value mismatch.", value, parameterProvider.getInteger(key)); } @Test public void getIntegerNegativeTest() { final String key = "unknown"; final int value = CmdParser.DEFAULT_INTEGER; assertEquals("Error, wrongly defined default value.", value, parameterProvider.getInteger(key)); }
The first method contains a positive test for obtaining text based on the key. The second method contains a negative test, which tests whether we get the default value if the key isn't found. The tests for obtaining a number are similar.
That would be all for today. In the next lesson, Java Server - Google Guice, we'll implement dependency management using the Google Guice library.