Lab Workbook: Building Distributes Applications with Java J2EE
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.
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 static void main(String[] args) {
ChatServer server = new ChatServer();
server.start(6666); // Use port 6666 for communication
}
}
#### **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;
}
}
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.
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
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.
Lab 2: Remote Method Invocation (RMI) – Building a Distributed Calculator
Long Game:
Servlets and JavaServer Pages
EJBs
Want to print your doc? This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (