-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
Java Server Interface is designed around a three-layer architectural model that prioritizes separation of concerns, modularity, and extensibility. Understanding this architecture is key to leveraging JSI's full potential.
┌─────────────────────────────────────────────────────────────┐
│ LAYER 3: Protocol Layer │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ Application-specific logic │
│ • HttpServer (routing, request parsing, response building) │
│ • DatabaseServer (query execution, storage management) │
│ • Your custom protocol implementation │
├─────────────────────────────────────────────────────────────┤
│ LAYER 2: Transport Layer │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ Connection management (protocol-agnostic) │
│ • ConnectionServer (TCP socket handling) │
│ • Thread-per-client model │
│ • Generic I/O utilities (file reading) │
├─────────────────────────────────────────────────────────────┤
│ LAYER 1: Foundation Layer │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ Core lifecycle and hooks │
│ • Server (abstract base class) │
│ • start(), stop() lifecycle methods │
│ • onBeforeStart(), onServerStarted() hooks │
└─────────────────────────────────────────────────────────────┘
The revolutionary aspect of JSI's architecture is that every layer is optional. You can:
- Use all three layers (quickest path for standard protocols)
- Skip Layer 2 and implement your own transport (e.g., UDP, WebSocket)
- Skip Layer 3 and build completely custom protocols
- Only use Layer 1 for maximum control
This flexibility means JSI adapts to your needs rather than forcing you into a specific architectural pattern.
File: Server.java
The absolute minimum abstraction. This layer defines what any server must do, regardless of transport or protocol:
public abstract class Server {
public abstract void start();
protected void onBeforeStart() {}
protected void onServerStarted() {}
public abstract Response handleRequest(Request request);
}Key Characteristics:
- No assumptions about transport (could be TCP, UDP, shared memory, message queues)
- No assumptions about protocol (could be HTTP, database queries, binary protocols)
- Pure lifecycle management: start server, inject hooks, handle requests
When to use directly:
- Building servers with non-standard transports (UDP, Unix sockets)
- Maximum control over every aspect of server behavior
- Educational purposes to understand server fundamentals
Example: See UDP chat server example
File: connection/ConnectionServer.java
Adds TCP connection management on top of the foundation:
public abstract class ConnectionServer extends Server {
private final int port;
public void start() {
// Creates ServerSocket, accepts connections
// Spawns thread for each client
}
protected abstract Request parseRequest(String input);
protected String readFile(String filePath) throws IOException {
// Generic file I/O utility
}
}Key Characteristics:
- Knows about sockets, nothing about protocols
- Implements standard TCP server pattern (listen → accept → spawn thread)
- Thread-per-client concurrency model
- Provides utilities (file reading) useful for many protocols
Threading Model:
Server Thread Client Thread 1 Client Thread 2
│ │ │
├─ accept() ────────────►│ │
│ ├─ parseRequest() │
│ ├─ handleRequest() │
│ └─ send response │
│ │
├─ accept() ────────────────────────────────────►│
│ ├─ parseRequest()
├─ accept() (blocked) ├─ handleRequest()
. └─ send response
.
Each client connection runs in its own thread, allowing concurrent request handling.
When to use directly:
- Building custom TCP-based protocols
- Need socket-level control but don't want to write accept loop
- Custom request/response parsing logic
Files:
Implements specific application protocols on top of the transport layer:
public abstract class HttpServer extends ConnectionServer {
@Override
protected HttpRequest parseRequest(String input) {
// Parse HTTP request format
}
@Override
public HttpResponse handleRequest(Request request) {
// Route to appropriate handler method
}
protected void registerRoutes() {
// Scan for @Route annotations
}
}Responsibilities:
- HTTP request parsing (method, path, headers, parameters)
- Annotation-based routing system
- Response serialization (status line, headers, body)
- Static file serving
- Error handling (404, 500, etc.)
public abstract class DatabaseServer extends ConnectionServer {
private DatabaseEngine databaseEngine;
@Override
public QueryResult handleRequest(Request query) {
return databaseEngine.execute((Query) query);
}
}Responsibilities:
- Query parsing (delegated to specific implementations like MySQL)
- Query execution through
DatabaseEngine - Storage management through
StorageEngine - Transaction support
When to use:
- Building HTTP REST APIs or web servers
- Creating database applications
- Standard protocol implementations
Throughout all layers, JSI uses a consistent Request/Response pattern:
Client Server
│ │
├──── Request ───────────►│
│ ├─ parseRequest()
│ ├─ handleRequest()
│ ├─ createResponse()
│◄──── Response ──────────┤
│ │
Core Interfaces:
public interface Request {
String serialize();
}
public interface Response {
String serialize();
}This abstraction allows any type of request/response to flow through the system:
-
HttpRequest/HttpResponsefor HTTP protocol -
Query/QueryResultfor database protocol - Your custom request/response types
See Core Abstractions for detailed documentation.
JSI's architecture exemplifies SOLID design principles:
Each layer has one reason to change:
- Layer 1: Changes only if fundamental server lifecycle logic changes
- Layer 2: Changes only if TCP socket handling strategy changes
- Layer 3: Changes only if protocol specifications change
Classes are open for extension, closed for modification:
- Extend
HttpServerto add new routes without modifying the base class - Implement
StorageEngineto add new persistence backends - No need to modify existing code to add features
Any ConnectionServer subclass can replace another:
ConnectionServer server = new HttpServer(8080);
// Can be replaced with:
ConnectionServer server = new DatabaseServer(3306, engine);
// Code using 'server' works identicallySmall, focused interfaces instead of monolithic ones:
-
StorageEngine- only storage operations -
DatabaseEngine- only query execution -
Request/Response- only serialization
High-level modules don't depend on low-level modules:
// High-level: DatabaseServer
// Low-level: JsonStorageEngine, XmlStorageEngine
// Both depend on abstraction: StorageEngine
DatabaseServer server = new DatabaseServer(port,
new DatabaseEngine(new JsonStorageEngine("./db")));Let's trace an HTTP request through all layers:
1. Client sends HTTP request to localhost:8080
GET /users?id=123 HTTP/1.1
▼
2. LAYER 2 (ConnectionServer)
• ServerSocket.accept() receives connection
• Spawns new thread for this client
• Reads raw input stream into String
▼
3. LAYER 3 (HttpServer)
• parseRequest(): Parses HTTP format
→ Creates HttpRequest object
→ Extracts path: "/users"
→ Extracts parameters: {"id": "123"}
▼
4. LAYER 3 (HttpServer - Routing)
• handleRequest(): Matches path "/users"
• Invokes @Route-annotated method
• Method receives HttpRequest object
▼
5. APPLICATION LOGIC (Your Code)
@Route(path = "/users")
public HttpResponse getUser(HttpRequest req) {
String id = req.getParameter("id");
// ... business logic ...
return createJsonResponse(OK, userData);
}
▼
6. LAYER 3 (HttpServer - Response)
• serialize(): Converts HttpResponse to string
HTTP/1.1 200 OK
Content-Type: application/json
{"id": 123, "name": "Alice"}
▼
7. LAYER 2 (ConnectionServer)
• Writes response to client socket
• Closes connection
• Thread terminates
// Start with JSON storage
StorageEngine storage = new JsonStorageEngine("./db");
DatabaseEngine engine = new DatabaseEngine(storage);
// Switch to XML storage - no other code changes!
StorageEngine storage = new XmlStorageEngine("./db");
DatabaseEngine engine = new DatabaseEngine(storage);// UDP-based server bypassing Layer 2
public class UdpChatServer extends Server {
@Override
public void start() {
DatagramSocket socket = new DatagramSocket(port);
// Custom UDP implementation
}
}// Use Layer 2 but custom protocol (not HTTP or Database)
public class GameServer extends ConnectionServer {
@Override
protected GameCommand parseRequest(String input) {
// Custom binary protocol parsing
}
@Override
public GameResponse handleRequest(Request request) {
// Custom game logic
}
}-
Core Abstractions - Deep dive into
Server,Client,Request,Response - ConnectionServer - TCP transport layer details
- HTTP Server - Protocol Layer: HTTP implementation
- Database Server - Protocol Layer: Database implementation
- Design Patterns - Patterns used throughout JSI
- Extensibility Guide - Building your own layers
Next: Explore Core Abstractions to understand the foundational interfaces, or jump to HTTP Server to see a complete protocol implementation.
JSI - Java Server Interface | Educational Server Framework | Zero Dependencies
Home • Getting Started • Architecture • Source Code
Made for learning | Report Issues • Discussions
Last updated: December 2025 | JSI v1.0
HTTP Development
Database Development
Custom Protocols