Share
Explore

Lab Workbook: Building the Chat Server with Java J2EE

megaphone

Lab 1: Socket Programming - Foundation for Distributed Systems

Preamble: The Journey to Enterprise Java

Welcome to CSD-4464! This course charts an exciting journey through the evolution of distributed systems, starting with the foundational building blocks and progressing to enterprise-grade architectures.
Think of it as climbing a mountain of abstraction - each layer building upon the previous one to create increasingly powerful and flexible systems.
The Knowledge Lineage ​In this course, we'll traverse five crucial layers of distributed computing:
1. **Sockets** (Where we are now) - The fundamental communication channel between networked processes 2. **Remote Method Invocation (RMI)** - Object-oriented abstraction over network communication 3. **JSP/JSF with Servlets** - Web-centric presentation and processing 4. **Enterprise JavaBeans (EJB)** - Component-based business logic and transaction management 5. **JAX Web Services** - Platform-independent service-oriented architecture
Once we get here, we will fold on JPA Java Persistance Architecture by connecting to a MySQL database and build a complete MVC application.
We begin with sockets because they represent the raw essence of network communication - the ability for two processes to exchange information across a network. Understanding sockets provides crucial insights into how all higher-level abstractions ultimately work.
Why This Progression Matters Modern enterprise applications are built using JAX-RS (REST) or JAX-WS (SOAP) web services, but these sophisticated frameworks abstract away the underlying network communication.
By starting with sockets, you'll gain a deep understanding of:
- How distributed systems fundamentally communicate - The challenges of network programming (latency, failures, concurrency) - Why higher-level abstractions are valuable - The trade-offs between simplicity and power at each layer
### Our Development Environment We'll use a minimalist but powerful toolkit: - MySQL for persistence - GlassFish as our Java EE application server - Sublime Text as our editor
We've chosen Sublime Text over more complex IDEs to maintain focus on the core concepts rather than IDE-specific workflows. This approach ensures you understand the fundamental principles without the overhead of learning complex tool chains.
## Lab 1 Instructions
### Objective Create a multi-client chat application using Java sockets, demonstrating fundamental network programming concepts and laying the groundwork for understanding distributed systems.
### Requirements
1. **Server Implementation (40 points)** - Create a server that accepts multiple client connections - Implement thread management for concurrent client handling - Maintain a list of connected clients - Properly handle client disconnections
2. **Client Implementation (30 points)** - Implement connection to server - Handle user input and server messages concurrently - Provide clean shutdown mechanism - Display appropriate status messages
3. **Protocol Implementation (20 points)** - Define clear message format for client-server communication - Implement user identification mechanism - Handle broadcast messages - Manage connection/disconnection messages
4. **Code Quality (10 points)** - Clear documentation and comments - Proper exception handling - Clean code organization - Following Java naming conventions
### Deliverables
1. **Source Code** - Server.java - Client.java - Any additional utility classes - README.md explaining how to run your application
2. **Documentation** - Brief design document explaining your implementation choices - Protocol specification - Known limitations or issues
3. **Test Results** - Screenshots showing multiple clients connected - Evidence of proper message broadcasting - Demonstration of graceful error handling
### Submission Guidelines
1. Create a ZIP file containing all deliverables 2. Name the file: `Lab1_StudentID_LastName.zip` 3. Submit through the designated course submission system 4. Ensure all code is properly commented and formatted
## Grading Rubric
### Server Implementation (40 points) - Multi-client support (15 points) - Excellent: Handles multiple clients flawlessly with proper thread management - Good: Handles multiple clients with minor issues - Fair: Basic multi-client support with significant limitations - Poor: Single client only or major issues
- Connection Management (15 points) - Excellent: Robust handling of connections/disconnections - Good: Proper connection management with minor issues - Fair: Basic connection management with some bugs - Poor: Unreliable connection management
- Error Handling (10 points) - Excellent: Comprehensive error handling and recovery - Good: Most error cases handled appropriately - Fair: Basic error handling - Poor: Minimal or no error handling
### Client Implementation (30 points) - User Interface (10 points) - Excellent: Clear, intuitive interface with status updates - Good: Functional interface with basic status information - Fair: Basic interface with minimal feedback - Poor: Confusing or broken interface
- Message Handling (10 points) - Excellent: Smooth message sending/receiving with proper threading - Good: Reliable message handling with minor issues - Fair: Basic message handling with some bugs - Poor: Unreliable message handling
- Resource Management (10 points) - Excellent: Proper cleanup of resources, graceful shutdown - Good: Most resources properly managed - Fair: Basic resource management - Poor: Resource leaks or improper cleanup
### Protocol Implementation (20 points) - Message Format (10 points) - Excellent: Well-defined, efficient protocol - Good: Clear protocol with minor inconsistencies - Fair: Basic protocol with some issues - Poor: Poorly defined or inconsistent protocol
- Feature Support (10 points) - Excellent: All required features implemented properly - Good: Most features implemented with minor issues - Fair: Basic feature implementation - Poor: Missing key features
### Code Quality (10 points) - Documentation (4 points) - Excellent: Comprehensive, clear documentation - Good: Adequate documentation with minor gaps - Fair: Basic documentation - Poor: Minimal or unclear documentation
- Code Organization (3 points) - Excellent: Well-organized, modular code - Good: Mostly organized code with some issues - Fair: Basic organization - Poor: Poor organization or structure
- Coding Standards (3 points) - Excellent: Follows all Java conventions - Good: Mostly follows conventions - Fair: Some adherence to conventions - Poor: Ignores conventions
## Mapping to Course Learning Outcomes
This lab specifically addresses the following course learning outcomes:
1. CLO 1.1: Configure and use testing frameworks - Implementation of basic testing for network communication 2. CLO 1.4: Implement source-control techniques - Organization and submission of code demonstrates version control practices
3. CLO 2.6: Utilize functional programming paradigm - Use of lambda expressions for thread handling
4. CLO 3.3: Manage network requests - Direct implementation of socket-based communication
5. CLO 3.9: Implement contexts and dependency injection - Basic introduction through socket connection management
Remember: This lab sets the foundation for understanding how distributed systems communicate at their most basic level. The skills you develop here will be crucial as we progress to more sophisticated architectures in subsequent labs.

Introduction

In Java, services are enabled by PORTS.

A Service is a METHOD CALL.
Calling a Method on a REMOTE JVM? You will be connect to the PORT in that REMOTE JVM for that METHOD.
HAND IN: A word Document with all code and screen shots demonstrating the successfuly operation of this illustration of how Java Sockets work, uploaded to the Moodle Assigment Page.
Due December 15.

info

Preamble: From Sockets to SOAP-Based Web Services

In this lab, you will build a fundamental networking application—a simple chat program using Java sockets.
This exercise introduces core concepts of object communication where the objects live in different JVMs connected by TCP/IP network.
Understanding how data flows between a client and server in this low-level setting is essential as we progress towards more advanced technologies like **Remote Method Invocation (RMI)** and **SOAP-based web services**.
Socket programming allows us to establish direct, low-level connections between systems, enabling real-time data exchange.
While this is effective for small, simple applications, real-world systems often require more sophisticated, robust, and scalable solutions.
As we move up the abstraction ladder, RMI will introduce the concept of calling methods on remote objects as if they were local, simplifying the complexity of network communication. Building on this, SOAP (Simple Object Access Protocol) offers a standardized, XML-based framework for exchanging structured data over a network, suitable for large-scale, distributed applications.
By learning socket communication first, you'll gain a deeper appreciation of how high-level web services—such as SOAP—are built on the same fundamental principles of client-server interaction.

SOAP services, unlike socket-based programs, add essential features such as platform independence, language-agnostic communication, and standardized messaging, all of which are vital for modern enterprise-level distributed applications.
This lab sets the foundation for that journey, connecting the dots from basic networking to sophisticated J2EE web services architecture.

Lesson 1: Introduction to Java Networking with Sockets

Welcome to your journey towards understanding J2EE web services!
In this initial step, we will lay the foundation by exploring a simple concept: a **socket-based chat application**.

This will help you grasp the basics of how text messages can be sent between computers over a TCP/IP connection, setting the stage for more advanced topics like Remote Method Invocation (RMI) in future lessons.
---
### **Objective** ​By the end of this lesson, you will: 1. Understand the concept of sockets and how they enable communication between two systems. 2. Implement a simple Java socket-based chat application. 3. Gain an appreciation of how TCP/IP networking underpins higher-level services like web services in J2EE.
---
### **Prerequisites** - Basic understanding of Java SE. - Familiarity with input/output streams, basic Java programming, and threads.
---
### **Tools Required** - **Java Development Kit (JDK)** (preferably version 11 or above). - Any Java Integrated Development Environment (IDE) such as Eclipse, IntelliJ IDEA, or NetBeans.

Concept Overview**

What are Sockets?

In networking, a **socket** is one endpoint of a two-way communication link between two programs running on a network.

It is bound to a port number so that the Transmission Control Protocol (TCP) layer can identify the application that data is destined for.
In simpler terms, sockets allow two computers (or programs) to "talk" to each other over the internet or a local network.

How Sockets Work A socket connection is established as follows:

1. **Server**: Waits for a client to connect.
2. **Client**: Initiates a connection to the server.
3. **Connection Established**: The server and client can now send and receive data.
4. **Data Transfer**: The client and server exchange messages (data).
5. **Close**: The connection is terminated when either side decides to close the socket.
This socket-to-socket communication forms the basis of many modern-day Internet applications, such as chat apps, file transfer systems, and even web services.
---

Step-by-Step Instructions

Step 1: Create the Server

First, we will create the server-side of the chat application. The server will wait for a connection from the client and, once connected, will facilitate the exchange of messages.
import java.io.*; import java.net.*;
public class ChatServer { private ServerSocket serverSocket; private Socket clientSocket; private PrintWriter out; private BufferedReader in;
public void start(int port) { try { serverSocket = new ServerSocket(port); System.out.println("Server started. Waiting for a client..."); clientSocket = serverSocket.accept(); // Wait for a client to connect System.out.println("Client connected."); // Streams for sending and receiving messages out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println("Client: " + inputLine); if ("exit".equalsIgnoreCase(inputLine)) { out.println("Goodbye!"); break; } out.println("Server: " + inputLine); } } catch (IOException e) { e.printStackTrace(); } finally { stop(); } }
public void stop() { try { in.close(); out.close(); clientSocket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } }
public static void main(String[] args) { ChatServer server = new ChatServer(); server.start(6666); // Use port 6666 for communication } } ​
image.png
#### **Step 2: Create the Client** Now, let's create the client-side of the application. The client will initiate the connection and then send and receive messages from the server.
```java import java.io.*; import java.net.*;
public class ChatClient { private Socket socket; private PrintWriter out; private BufferedReader in;
public void start(String ip, int port) { try { socket = new Socket(ip, port); // Connect to the server System.out.println("Connected to the server.");
out = new PrintWriter(socket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); String userInput; System.out.println("Type your message (type 'exit' to quit):"); while ((userInput = stdIn.readLine()) != null) { out.println(userInput); // Send user input to server System.out.println(in.readLine()); // Receive and print server's response if ("exit".equalsIgnoreCase(userInput)) { break; } }
} catch (IOException e) { e.printStackTrace(); } finally { stop(); } }
public void stop() { try { in.close(); out.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }
public static void main(String[] args) { ChatClient client = new ChatClient(); client.start("127.0.0.1", 6666); // Connect to localhost on port 6666 } } ```
---
### **Step 3: Run the Application** 1. **Run the Server**: - Compile and run the `ChatServer` class. - The server will start and wait for a client to connect. 2. **Run the Client**: - In a separate terminal or IDE instance, compile and run the `ChatClient` class. - The client will connect to the server and begin sending messages. 3. **Exchange Messages**: - The client and server can now exchange messages. Type a message in the client and see it appear in the server's console, and vice versa.
---
### **Key Concepts** - **Sockets**: Provide communication between two machines using TCP/IP. - **Input/Output Streams**: Enable reading from and writing to sockets. - **Threading**: In a more complex version, we could use threads to handle multiple clients simultaneously, but for now, we'll stick to a single client-server setup.
---
### **Exercise: Extend the Application** To solidify your understanding, try the following: - **Multiple Clients**: Modify the server to handle multiple clients simultaneously using threads. - **Enhanced Messaging**: Add features like message formatting or user nicknames. - **Error Handling**: Improve the robustness of your application by handling various exceptions.
---
### **Conclusion** By implementing this simple socket-based chat application, you now have a solid foundation in Java networking. In the next lesson, we will build on this concept to explore Remote Method Invocation (RMI) and eventually move towards J2EE web services.
This step-by-step approach ensures that by the time we reach the complexities of J2EE, you will have a clear understanding of how lower-level networking concepts like sockets play a role in high-level web services.


info

Multi-client Chat

Lab Update: Adding Multi-Client Support with User Identification

We will now extend the previous socket-based chat application to allow multiple clients to connect simultaneously.
Each client will enter a username upon connection, which will be used to identify them in the chat.
The server will manage multiple client connections by utilizing threads, allowing for concurrent communication between several clients.

Updated Objective

By the end of this lesson, you will:
Implement multi-client support in a socket-based Java chat application using threads.
Allow clients to enter a username, which will be used to identify them in the chat.
Broadcast messages from one client to all connected clients, simulating a group chat.

Step 1: Modify the Server for Multi-Client Support

To handle multiple clients, the server needs to spawn a new thread for each incoming client connection.
This way, multiple clients can interact with the server simultaneously.
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatServer {
private ServerSocket serverSocket;
private Set<ClientHandler> clientHandlers = new HashSet<>();

public void start(int port) {
try {
serverSocket = new ServerSocket(port);
System.out.println("Server started. Waiting for clients...");

while (true) {
Socket clientSocket = serverSocket.accept();
ClientHandler clientHandler = new ClientHandler(clientSocket, this);
clientHandlers.add(clientHandler);
new Thread(clientHandler).start(); // Start a new thread for each client
System.out.println("New client connected.");
}
} catch (IOException e) {
e.printStackTrace();
}
}

public synchronized void broadcastMessage(String message, ClientHandler sender) {
for (ClientHandler clientHandler : clientHandlers) {
if (clientHandler != sender) { // Send to all except the sender
clientHandler.sendMessage(message);
}
}
}

public synchronized void removeClient(ClientHandler clientHandler) {
clientHandlers.remove(clientHandler);
}

public static void main(String[] args) {
ChatServer server = new ChatServer();
server.start(6666); // Use port 6666 for communication
}
}

class ClientHandler implements Runnable {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private String username;
private ChatServer server;

public ClientHandler(Socket socket, ChatServer server) {
this.socket = socket;
this.server = server;
}

@Override
public void run() {
try {
// Setup I/O streams
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

// Ask the client for a username
out.println("Enter your username:");
username = in.readLine();
System.out.println(username + " has joined the chat.");
server.broadcastMessage(username + " has joined the chat.", this);

// Listen for messages from the client and broadcast them
String message;
while ((message = in.readLine()) != null) {
if ("exit".equalsIgnoreCase(message)) {
out.println("Goodbye!");
break;
}
String formattedMessage = username + ": " + message;
System.out.println(formattedMessage); // Log the message to the server console
server.broadcastMessage(formattedMessage, this); // Broadcast to other clients
}
} catch (IOException e) {
e.printStackTrace();
} finally {
server.removeClient(this);
server.broadcastMessage(username + " has left the chat.", this);
try {
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

public void sendMessage(String message) {
out.println(message);
}
}

Step 2: Modify the Client for User Identification

The client now asks for a username when connecting, which is used to identify the user in chat messages. Additionally, the client will receive and display messages broadcast from other clients.
import java.io.*;
import java.net.*;

public class ChatClient {
private Socket socket;
private PrintWriter out;
private BufferedReader in;

public void start(String ip, int port) {
try {
socket = new Socket(ip, port); // Connect to the server
System.out.println("Connected to the server.");

out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));

// Receive and display the server's prompt for a username
System.out.println(in.readLine());
String username = stdIn.readLine();
out.println(username); // Send username to the server

// Create a thread to listen for messages from the server
new Thread(new ServerListener(in)).start();

// Read user input and send it to the server
String userInput;
System.out.println("You can now start chatting. Type 'exit' to quit:");
while ((userInput = stdIn.readLine()) != null) {
out.println(userInput); // Send user input to the server
if ("exit".equalsIgnoreCase(userInput)) {
break;
}
}

} catch (IOException e) {
e.printStackTrace();
} finally {
stop();
}
}

public void stop() {
try {
in.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
ChatClient client = new ChatClient();
client.start("10.51.210.118", 6666); // Connect to localhost on port 6666
}

// Inner class to listen for server messages
private static class ServerListener implements Runnable {
private BufferedReader in;

public ServerListener(BufferedReader in) {
this.in = in;
}

@Override
public void run() {
String serverMessage;
try {
while ((serverMessage = in.readLine()) != null) {
System.out.println(serverMessage); // Print messages from the server
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Step 3: Run the Multi-Client Chat Application

Run the Server:
Compile and run the ChatServer class.
The server will start and wait for clients to connect.
Run the Clients:
Open multiple instances of the ChatClient class in different terminal windows or IDE instances.
Each client will be prompted to enter their username and start chatting.
Chat!:
Messages from one client will be broadcast to all other connected clients.
Type "exit" to leave the chat.

Key Enhancements

Threading: The server can handle multiple clients simultaneously by spawning a new thread for each client.
User Identification: Each client is asked for a username when they connect, which is used to identify them in chat messages.
Broadcasting: Messages from one client are broadcast to all other connected clients.

Conclusion

This multi-client chat application introduces the concept of concurrent networking using threads.

This is a foundational step toward building more complex, distributed applications like web services.
In future lessons, we will explore Remote Method Invocation (RMI) and how to call methods on remote objects, building on the communication principles demonstrated here.

Eventually, this will evolve into full-fledged SOAP-based web services in the J2EE environment, enabling structured, scalable communication across distributed systems.

megaphone

Lab 2: Remote Method Invocation (RMI) – Building a Distributed Calculator


Long Game:
Servlets and JavaServer Pages
EJBs
Databases
SOAP and WSDL
Project: Build a SOAP microservices architecture with an Enterprise Service Bus
In this lab, we will build a Remote Method Invocation (RMI) application, where a server hosts a calculator service that provides an add() method.
Multiple clients will connect to the server, send two numbers to the calculator, and receive the result of their addition.
This lab builds upon the socket-based communication you learned in the previous lab.
While sockets enable direct data transfer between systems, RMI abstracts this concept by allowing remote method calls as if the methods were running locally.
Behind the scenes, RMI uses network communication (TCP/IP, often via sockets) to facilitate these remote calls.

Objective

By the end of this lesson, you will:
Understand the concept of Remote Method Invocation (RMI) in Java.
Implement a simple RMI-based calculator that supports multiple clients.
Relate the workings of RMI back to socket-based communication, highlighting how RMI uses sockets under the hood for method calls.

Prerequisites

Basic understanding of Java SE.
Completion of Lab 1 (socket-based chat application).
Familiarity with interfaces, exceptions, and basic networking.

Tools Required

Java Development Kit (JDK) (preferably version 11 or above).
Any Java Integrated Development Environment (IDE) such as Eclipse, IntelliJ IDEA, or NetBeans.

Concept Overview

What is Remote Method Invocation (RMI)?

RMI allows Java objects to invoke methods on an object located on a different machine or JVM (Java Virtual Machine).
This simplifies distributed computing by abstracting the complexity of network communication.
When a client calls a method on a remote object, the RMI framework manages the underlying socket communication between the client and the server, as well as the serialization and deserialization of method arguments and return values.

Step 1: Define the Remote Interface

We begin by defining the interface for the calculator service.
This interface will include the add() method, which the clients will call remotely.
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {
// A method to add two numbers, which can be called remotely
int add(int a, int b) throws RemoteException;
}

In this interface:
Remote: All RMI interfaces must extend Remote to signal that they can be accessed remotely.
RemoteException: All remote methods must declare the possibility of throwing RemoteException to handle network-related errors.

Step 2: Implement the Remote Calculator

Next, implement the server-side class that provides the actual logic for the add() method.
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

// The Calculator implementation extends UnicastRemoteObject to support RMI
public class CalculatorImpl extends UnicastRemoteObject implements Calculator {

protected CalculatorImpl() throws RemoteException {
super();
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.