Java Project: Tiny Quote Server
This project builds a tiny quote server from the machinery of this course. A client sends one line naming a quote number, and the server replies with that quote.
The quote store
A ConcurrentHashMap holds three quotes by number. The program looks up number 2 and a missing number.
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
ConcurrentHashMap<Integer, String> quotes = new ConcurrentHashMap<>();
quotes.put(1, "Small steps every day.");
quotes.put(2, "Make it work, then make it clear.");
quotes.put(3, "Programs are read more than written.");
System.out.println("Quote 2: " + quotes.getOrDefault(2, "Unknown quote"));
System.out.println("Quote 9: " + quotes.getOrDefault(9, "Unknown quote"));
}
}A pool and an atomic count
A fixed pool of two threads runs four small tasks. Each task adds one to an AtomicInteger, and the main thread prints the final count.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(2);
AtomicInteger count = new AtomicInteger();
for (int i = 0; i < 4; i++) {
pool.submit(() -> {
count.incrementAndGet();
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("Count: " + count.get());
}
}Example
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
public static void main(String[] args) throws Exception {
ConcurrentHashMap<Integer, String> quotes = new ConcurrentHashMap<>();
quotes.put(1, "Small steps every day.");
quotes.put(2, "Make it work, then make it clear.");
quotes.put(3, "Programs are read more than written.");
String[] requests = new String[4];
AtomicInteger ticket = new AtomicInteger();
AtomicInteger served = new AtomicInteger();
CountDownLatch done = new CountDownLatch(4);
ExecutorService pool = Executors.newFixedThreadPool(2);
try (ServerSocket server = new ServerSocket(0)) {
System.out.println("PORT " + server.getLocalPort());
for (int i = 0; i < 4; i++) {
Socket socket = server.accept();
int slot = ticket.getAndIncrement();
pool.submit(() -> {
try (socket;
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true, StandardCharsets.UTF_8)) {
String line = reader.readLine();
requests[slot] = line;
writer.println(lookup(quotes, line));
served.incrementAndGet();
} catch (Exception e) {
System.out.println("Handler error: " + e);
} finally {
done.countDown();
}
});
}
done.await();
} finally {
pool.shutdown();
}
for (String request : requests) {
System.out.println("Request: " + request);
}
System.out.println("Requests served: " + served.get());
}
static String lookup(ConcurrentHashMap<Integer, String> quotes, String line) {
if (line != null && line.startsWith("quote ")) {
try {
int number = Integer.parseInt(line.substring(6).trim());
return quotes.getOrDefault(number, "Unknown quote");
} catch (NumberFormatException e) {
return "Unknown quote";
}
}
return "Unknown quote";
}
}Key points
- A ConcurrentHashMap holds the quotes by number.
- A fixed pool of two worker threads serves the connections.
- Each handler reads one line and writes one reply line.
- An AtomicInteger hands out one slot number per accepted connection.
- Handlers record their request text into their own slot and never print.
- The main thread prints the recorded requests in slot order after a latch reaches zero.
- The final count is exact because the main thread prints it after every handler has finished.
In this example, the server binds port 0, serves four connections through a two-thread pool, records each request in its ticket slot, and the main thread prints the four requests in order plus the exact served count.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why is the printed served count exact?