Workshop: Create a Java-based Chatbot Application

In this workshop we create a simple Java-based Chatbot Application using a server and a command line client. The command line client will communicate with the server using JSON in standard HTTP requests. The server responds with a simple JSON containing the answer of the user request.

Background

This section provides all the essential theory and background knowledge you need to solve the following workshop. It assumes you already have basic Java knowledge (classes, methods, loops, strings, exceptions, packages). No prior experience with HTTP or Restlet is required.

1. Core Concepts

REST APIs and the Chatbot Endpoint

  • A REST API is a web service that uses standard HTTP methods (GET, POST, PUT, DELETE).
  • In this exercise we only need POST because the client is sending data (the user message) to the server.
  • The endpoint is http://localhost:8081/chatbot.
  • Data is exchanged in JSON format (simple key-value objects inside { }).
  • The client sends:JSON{ "impersonate": "Albert Einstein", "request": "Hello" }
  • The server replies with a JSON object containing the AI response.

HTTP Basic Authentication

  • The simplest way to protect a web endpoint.
  • The client sends an Authorization header with the word Basic followed by a Base64-encoded string of username:password.
  • Example: admin:secret123 → Base64 → YWRtaW46c2VjcmV0MTIz → header becomes: Authorization: Basic YWRtaW46c2VjcmV0MTIz
  • On the server, we decode it and compare against hardcoded values.
  • This is not secure for production (no encryption), but perfect for a mock server in an exam.

2. Testing with curl – Very Important for Development & Debugging

Before writing the Java client, or while developing the server, you should test your server using curl (Command Line tool). This is the fastest way to verify your server works correctly.

Basic curl Commands for This Project

1. Simple POST request without authentication:

curl -X POST http://localhost:8081/chatbot \
     -H "Content-Type: application/json" \
     -d '{"impersonate":"Albert Einstein", "request":"What is gravity?"}'

2. POST request with Basic Authentication (most important):

curl -X POST http://localhost:8081/chatbot \
     -H "Content-Type: application/json" \
     -H "Authorization: Basic YWRtaW46c2VjcmV0MTIz" \
     -d '{"impersonate":"Marie Curie", "request":"Tell me about radioactivity"}'
  • YWRtaW46c2VjcmV0MTIz is the Base64 encoding of admin:secret123

3. How to generate the Base64 value yourself:

# On Linux / macOS
echo -n "admin:secret123" | base64

# On Windows (PowerShell)
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("admin:secret123"))

4. Useful curl options for testing:

OptionPurpose
-X POSTSpecify HTTP method
-H “Content-Type: …”Set request header
-d ‘{…}’Send JSON body
-vVerbose – shows full request & response
-u admin:secret123Alternative way (curl handles Base64)

Example with -u (easiest for testing):

curl -X POST http://localhost:8081/chatbot \
     -u admin:secret123 \
     -H "Content-Type: application/json" \
     -d '{"impersonate":"Tesla", "request":"What is AC current?"}'

2. Theory AI Bot Client (Java SE 11+)

Console Input (java.io.Console)

  • System.console() returns a Console object that reads directly from the terminal.
  • readLine(prompt) – prints the prompt and returns what the user types.
  • readPassword(prompt) – prints the prompt but does not echo the characters (stars or nothing appear). This is the secure way to read passwords.
  • new String(cnsl.readPassword(…)) converts the char[] to a normal String for later use.

Modern HTTP Client (java.net.http – since Java 11)

  • Replaces the old HttpURLConnection.
  • Three main classes you must know:
    • HttpClient – the reusable connection pool (created once with newHttpClient()).
    • HttpRequest – built with the fluent builder pattern:JavaHttpRequest.newBuilder() .uri(new URI("http://...")) .POST(...) // sends data .header("Authorization", "...") .build();
    • HttpResponse<String> – received with client.send(request, BodyHandlers.ofString()).
  • Body publishing: HttpRequest.BodyPublishers.ofString(jsonString) sends the JSON as the request body.

Simple JSON Handling (Exam-style)

  • We build the JSON manually with string concatenation (no external libraries needed).
  • We parse the server response with a very simple split(“:”) trick because the server returns a predictable tiny JSON.
  • Note: In real projects you would use Jackson or Gson, but the exam wants you to keep it minimal.

Exception Handling

  • The whole request is inside a try-catch(Exception e) so the chat loop never crashes if the server is down or the JSON is malformed.

3. Theory for Mock Chatbot Server (Restlet Framework)

What is Restlet?

  • A lightweight, pure-Java framework for creating REST web services (no heavy containers like Tomcat).
  • Very popular in university courses and older enterprise projects.
  • Core idea: You attach small “Restlet” objects to URL paths.

Main Restlet Building Blocks

ClassPurposeHow you use it in the exam
ComponentThe entire server (contains servers, hosts, etc.)Create once, add HTTP server on port 8081
RestletA handler for one URL pathOverride handle(Request, Response)
RequestIncoming HTTP request (method, headers, body)Check getMethod(), read body with getEntityAsText()
ResponseWhat you send back (status, headers, body)setStatus(), setEntity(json, MediaType.APPLICATION_JSON)
ChallengeResponseContains username/password for Basic Authrequest.getChallengeResponse()

How Authentication Works in Restlet

  • The client automatically sends the Authorization header.
  • Restlet parses it into a ChallengeResponse object.
  • You simply compare:Javachallenge.getIdentifier() // username new String(challenge.getSecret()) // password
  • If wrong → response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED).

Generating the Mock Response

  • Read the incoming JSON as plain text (request.getEntityAsText()).
  • Use String.indexOf() to extract the “impersonate” value (simple mock parsing – no JSON library required).
  • Add a random number with new Random().nextInt(1000) + 1.
  • Build the response JSON with String.format() and set it as the entity with MediaType.APPLICATION_JSON.

Starting the Server

Component component = new Component();
component.getServers().add(Protocol.HTTP, 8081);
component.getDefaultHost().attach("/chatbot", myRestlet);
component.start();

This single main method starts a complete web server on port 8081.

4. Why These Designs Are Used

  • No external libraries except Restlet JARs (assumed on classpath).
  • Minimal code that still meets every single requirement.
  • Clear separation: Client only talks to the server; Server only validates and replies.
  • Hardcoded values (username/password, mock message) so the program works immediately without a database.
  • Random number makes every reply slightly different, simulating a real AI.

You now have all the theory required. Study the classes and methods above, then try to write the programs yourself before looking at the provided solutions. Good luck!

Task 1: Create a Mockup-Server

You are tasked with developing a mock Chatbot Server using the Restlet Framework (Java). For the purpose of this workshop the server will only respond with dummy message. In practise however, you could easily include an AI-API, like xAI.

Functional Requirements:

  1. Server Configuration
    • Create a Restlet server that listens on http://localhost:8081.
    • The chatbot endpoint should be available at /chatbot.
  2. HTTP Basic Authentication
    • Implement hardcoded authentication.
    • Accept only the following credentials:
      • Username: admin
      • Password: secret123
    • Return HTTP 401 Unauthorized with appropriate message if credentials are wrong or missing.
  3. Request Handling
    • Accept only POST requests.
    • Expect a JSON body in the following format:JSON{ "impersonate": "Albert Einstein", "request": "What is the theory of relativity?" }
    • You may read the request body as a String (no need for full JSON deserialization).
  4. Response
    • Respond with a simple JSON object containing a hardcoded message plus a random number (between 1 and 1000) to simulate dynamic AI response.
    • Example response:JSON{ "response": "As Albert Einstein, I would say that imagination is more important than knowledge. [Random: 742]" }
    • Set proper Content-Type: application/json header.
  5. Error Handling & Code Quality
    • Handle exceptions gracefully.
    • Use clean code structure with proper package and class names.
Solution: Task 1: Create a Mockup-Server

Expected Solution

Here is the complete working solution that fulfills all the requirements:

package bytegrammer.javaaibotserver;

import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Status;
import org.restlet.engine.header.Header;
import org.restlet.util.Series;

import java.util.Random;

public class MockChatbotServer {

    private static final String VALID_USERNAME = "admin";
    private static final String VALID_PASSWORD = "secret123";

    public static void main(String[] args) throws Exception {
        Component component = new Component();
        component.getServers().add(org.restlet.data.Protocol.HTTP, 8081);

        component.getDefaultHost().attach("/chatbot", new Restlet() {
            @Override
            public void handle(Request request, Response response) {
                // Check if method is POST
                if (!request.getMethod().equals(Method.POST)) {
                    response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
                    return;
                }

                // Basic Authentication Check
                ChallengeResponse challenge = request.getChallengeResponse();
                if (challenge == null || 
                    !VALID_USERNAME.equals(challenge.getIdentifier()) || 
                    !VALID_PASSWORD.equals(new String(challenge.getSecret()))) {
                    
                    response.setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
                    response.setEntity("{\"error\":\"Unauthorized - Invalid credentials\"}", MediaType.APPLICATION_JSON);
                    return;
                }

                // Read request body
                String body = request.getEntityAsText();
                
                // Extract impersonate (simple string search for mock)
                String impersonate = "Unknown";
                if (body.contains("\"impersonate\":\"")) {
                    int start = body.indexOf("\"impersonate\":\"") + 15;
                    int end = body.indexOf("\"", start);
                    if (end > start) impersonate = body.substring(start, end);
                }

                // Generate random number
                int randomValue = new Random().nextInt(1000) + 1;

                // Create response
                String jsonResponse = String.format(
                    "{\"response\":\"Hello! I am %s. This is a mock response from the AI server. [Random: %d]\"}", 
                    impersonate, randomValue);

                response.setEntity(jsonResponse, MediaType.APPLICATION_JSON);
                response.setStatus(Status.SUCCESS_OK);
            }
        });

        component.start();
        System.out.println("✅ Mock AI Chatbot Server started on http://localhost:8081/chatbot");
        System.out.println("Username: admin | Password: secret123");
    }
}

Additional Short Questions (Optional)

Q2. Explain the role of ChallengeResponse in Restlet for handling Basic Authentication.

Q3. What are two major limitations of the current JSON handling approach in the server? How would you improve it using Jackson or Gson?

Q4. How would you modify the server to support multiple user accounts instead of hardcoded credentials?

Task 2: Create a Command-Line Client

You are required to develop a command-line AI Chatbot Client in Java that allows a user to interact with a remote chatbot server running at http://localhost:8081/chatbot.

Requirements:

  1. User Authentication
    • Prompt the user to enter a username and password securely (password should not be echoed on screen).
    • Use HTTP Basic Authentication for all requests.
  2. Impersonation Feature
    • Ask the user to enter the name of the person/AI character they want to impersonate (e.g., “Albert Einstein”, “Socrates”, etc.).
  3. Chat Functionality
    • Continuously read user messages from the console.
    • Send each message to the server using a POST request with the following JSON body:JSON{ "impersonate": "<personToImpersonate>", "request": "<userMessage>" }
    • Use Java’s modern HttpClient API (introduced in Java 11).
    • Display the chatbot’s response in the format: PersonToImpersonate : <response>
  4. Error Handling
    • Handle exceptions gracefully and display a user-friendly message if something goes wrong.
  5. Code Quality & Structure
    • Proper package declaration, imports, and clean code structure.
Solution Task 2:

The complete working solution that fulfills all requirements is the following program:

package bytegrammer.javaaibotclient;

import java.io.Console;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.Base64;

public class AIBotClient {

    public static void main(String[] args) {
        System.out.println("Hello this is the AI Bot Java Client.");
        
        Console cnsl = System.console();
        if (cnsl == null) {
            System.out.println("No console available");
            return;
        }

        String username = cnsl.readLine("Enter username : ");
        String password = new String(cnsl.readPassword("Enter password : "));
        String personToImpersonate = cnsl.readLine("Enter person to impersonate : ");

        System.out.println("Please start talking!");
        
        HttpClient client = HttpClient.newHttpClient();

        while (true) {
            String userMessage = cnsl.readLine(username + " : ");

            try {
                HttpRequest request = HttpRequest.newBuilder()
                    .uri(new URI("http://localhost:8081/chatbot"))
                    .POST(HttpRequest.BodyPublishers.ofString(
                        "{ \"impersonate\":\"" + personToImpersonate 
                        + "\", \"request\":\"" + userMessage + "\"}"))
                    .header("Authorization", getBasicAuthenticationHeader(username, password))
                    .build();

                HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

                String[] parts = response.body()
                    .replaceAll("\\{", "")
                    .replaceAll("\\}", "")
                    .trim()
                    .split(":");

                if (parts.length != 2) {
                    throw new IllegalStateException("Server responded with invalid format.");
                }

                System.out.println(personToImpersonate + " : " + parts[1]);

            } catch (Exception e) {
                System.out.println("Something went wrong: " + e.getMessage());
            }
        }
    }

    private static final String getBasicAuthenticationHeader(String username, String password) {
        String valueToEncode = username + ":" + password;
        return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes());
    }
}

Additional Short Questions

Q2. Explain why System.console().readPassword() is preferred over Scanner for reading passwords.

Q3. What is the purpose of Base64.getEncoder().encodeToString() in this program?

Q4. Identify and explain two security or robustness issues in the JSON construction part of the code. Suggest improvements.

Q5. How would you modify the code to support JSON escaping for user messages containing quotes or special characters?

Leave a Reply