A simple web application using Jakarta EE Servlets with content-type based request processing, packaged as an executable JAR with embedded Apache Tomcat.
- Content-Type Request Processors: Automatic routing based on request Content-Type header
- File Upload Processor (multipart/form-data)
- Form Data Processor (application/x-www-form-urlencoded)
- JSON Data Processor (application/json)
- JavaScript Execution Engine (application/javascript) - Server-side JS with Rhino
- Template Rendering Engine (text/html) - Custom template engine with variable substitution
- Interactive Script Editor: Web-based JavaScript code editor with real-time execution
- Performance Monitoring: Execution time and memory usage tracking for scripts
- YAML Configuration: Externalized configuration with environment variable support
- Health & Metrics: Built-in monitoring endpoints
- Strategy Pattern: Clean, extensible architecture for request processing
- Pure Jakarta EE: No frameworks, just servlets (with Gson for JSON and SnakeYAML for config)
- Embedded Tomcat: Self-contained executable JAR
- Java 17 or higher
- Maven 3.6+
servlet-example/
├── pom.xml
├── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── Main.java
│ │ └── servlet/
│ │ ├── RouterServlet.java
│ │ ├── processor/
│ │ │ ├── RequestProcessor.java (interface)
│ │ │ ├── ProcessorRegistry.java
│ │ │ ├── ProcessorResponse.java
│ │ │ ├── FileUploadProcessor.java
│ │ │ ├── FormDataProcessor.java
│ │ │ ├── JsonDataProcessor.java
│ │ │ ├── ScriptProcessor.java
│ │ │ └── TemplateProcessor.java
│ │ └── util/
│ │ ├── JsonUtil.java
│ │ ├── PropertiesUtil.java
│ │ └── TemplateEngine.java
│ └── resources/
│ ├── application.yml
│ └── static/
│ └── script-editor.html
Configuration is managed through src/main/resources/application.yml:
# Server Configuration
server:
port: ${SERVER_PORT:8080} # Supports environment variable override
# File Upload Configuration
upload:
maxFileSize: 10485760 # 10 MB
maxRequestSize: 52428800 # 50 MB
fileSizeThreshold: 1048576 # 1 MB
tempDirectory: ${java.io.tmpdir}
# Thread Pool Configuration
threadPool:
maxThreads: 200
minSpareThreads: 10
acceptCount: 100
connectionTimeout: 20000Override configuration using environment variables:
# Change server port
SERVER_PORT=9090 java -jar target/servlet-example.jar
# Or for development
SERVER_PORT=9090 mvn -PappRunmvn clean packageProduction mode (executable JAR):
java -jar target/servlet-example.jarDevelopment mode (similar to bootRun):
mvn -PappRunOr alternatively:
mvn compile exec:javaThe application will start and display:
=========================================
Tomcat server started successfully!
Port: 8080
GET Endpoints:
- http://localhost:8080/
- http://localhost:8080/health
- http://localhost:8080/metrics
- http://localhost:8080/script-editor (Interactive JavaScript Code Editor)
POST Endpoints:
- http://localhost:8080/api/form (Content-Type: application/x-www-form-urlencoded)
- http://localhost:8080/api/json (Content-Type: application/json)
- http://localhost:8080/api/upload (Content-Type: multipart/form-data)
- http://localhost:8080/api/script (Content-Type: application/javascript)
- http://localhost:8080/api/render (Content-Type: text/html)
=========================================
- URL:
http://localhost:8080/ - Method: GET
- Response: Welcome message with available endpoints
curl http://localhost:8080/{
"message": "Welcome to Jakarta EE Servlet Application",
"version": "1.0",
"endpoints": {
"GET": ["/", "/health", "/metrics", "/script-editor"],
"POST": ["/api/form", "/api/json", "/api/upload", "/api/script", "/api/render"]
},
"timestamp": 1234567890
}- URL:
http://localhost:8080/health - Method: GET
- Response: Application health status
curl http://localhost:8080/health{
"status": "UP",
"timestamp": 1234567890,
"uptime": "123456 ms"
}- URL:
http://localhost:8080/metrics - Method: GET
- Response: System metrics
curl http://localhost:8080/metrics{
"metrics": {
"totalRequests": 42,
"memory": {
"used": 12345678,
"free": 87654321,
"total": 100000000,
"max": 200000000
},
"threads": {
"active": 10
},
"timestamp": 1234567890
}
}- URL:
http://localhost:8080/api/form - Method: POST
- Content-Type:
application/x-www-form-urlencoded
curl -X POST http://localhost:8080/api/form \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "name=John&[email protected]&age=30"Response:
{
"status": "success",
"data": {
"name": "John",
"email": "[email protected]",
"age": "30"
},
"timestamp": 1234567890
}- URL:
http://localhost:8080/api/json - Method: POST
- Content-Type:
application/json
curl -X POST http://localhost:8080/api/json \
-H "Content-Type: application/json" \
-d '{"name":"Jane","email":"[email protected]","preferences":{"theme":"dark"}}'Response:
{
"status": "success",
"data": {
"received": {
"name": "Jane",
"email": "[email protected]",
"preferences": {
"theme": "dark"
}
},
"size": 75
},
"timestamp": 1234567890
}- URL:
http://localhost:8080/api/upload - Method: POST
- Content-Type:
multipart/form-data - Max File Size: 10 MB (configurable)
curl -X POST http://localhost:8080/api/upload \
-F "file=@/path/to/file.txt" \
-F "description=Test upload"Response:
{
"status": "success",
"data": {
"files": [
{
"fieldName": "file",
"fileName": "file.txt",
"size": 1234,
"contentType": "text/plain",
"savedPath": "/tmp/upload-123456-file.txt"
}
],
"fields": {
"description": "Test upload"
},
"fileCount": 1
},
"timestamp": 1234567890
}- URL:
http://localhost:8080/api/script - Method: POST
- Content-Type:
application/javascript - Features: Server-side JavaScript execution with performance monitoring
curl -X POST http://localhost:8080/api/script \
-H "Content-Type: application/javascript" \
-d '{
"script": "var sum = 0; for (var i = 1; i <= 100; i++) { sum += i; } console.log(\"Sum:\", sum); sum;",
"params": {}
}'Response:
{
"status": "success",
"data": {
"result": 5050.0,
"console": [
"Sum: 5050"
],
"executionTimeMs": 3,
"memoryUsedBytes": 1024
},
"timestamp": 1234567890
}Performance Metrics:
executionTimeMs: Script execution time in millisecondsmemoryUsedBytes: Memory consumed during execution in bytes
Security Features:
- Configurable timeout (default: 5000ms)
- Memory limit enforcement (default: 10MB)
- Interpreted mode (optimization level -1) for security
- Instruction observation for resource monitoring
Available Context:
request.method: HTTP methodrequest.path: Request pathrequest.remoteAddr: Client IP addressrequest.queryParams: Query parametersconsole.log(): Captures output to response
Java Interop: Scripts can create and use Java objects via Rhino with a secure whitelist/blacklist system:
Allowed Classes (Whitelist):
- Collections:
ArrayList,HashMap,HashSet,LinkedList,TreeMap,TreeSet, etc. - Utilities:
Date,UUID,Optional,Arrays,Collections - Primitives/Wrappers:
String,StringBuilder,Math,Integer,Long,Double, etc. - Date/Time:
LocalDate,LocalDateTime,Instant,Duration,Period, etc. - Math:
BigDecimal,BigInteger
Blocked for Security (Blacklist):
- System access:
System,Runtime,ProcessBuilder,Thread,ClassLoader - File I/O:
java.io.*,java.nio.file.* - Network:
java.net.* - Reflection:
java.lang.reflect.* - Database:
java.sql.*,javax.sql.*
// Allowed - Collection manipulation
var list = new java.util.ArrayList();
list.add("Hello");
list.add(42);
list.get(0); // Returns "Hello"
var map = new java.util.HashMap();
map.put("name", "Alice");
map.get("name"); // Returns "Alice"
// Blocked - System/File/Network access will fail
java.lang.System.exit(0); // ERROR: blocked
new java.io.File("/etc/passwd"); // ERROR: blocked
new java.net.Socket("host", 80); // ERROR: blocked- URL:
http://localhost:8080/script-editor - Method: GET
- Description: Web-based JavaScript code editor with syntax highlighting and real-time execution
Features:
- Live code execution with Ctrl+Enter
- Performance metrics display (execution time and memory usage)
- Console output capture
- Pre-loaded examples (Fibonacci, arrays, Java interop, etc.)
- Dark theme code editor
- URL:
http://localhost:8080/api/render - Method: POST
- Content-Type:
text/html - Description: Custom template engine with variable substitution and loops
curl -X POST http://localhost:8080/api/render \
-H "Content-Type: text/html" \
-d '{
"template": "<h1>{{title}}</h1><ul>{{#for user in users}}<li>{{user.name}}</li>{{/for}}</ul>",
"data": {
"title": "Users",
"users": [
{"name": "Alice"},
{"name": "Bob"}
]
}
}'Response:
{
"status": "success",
"data": {
"html": "<h1>Users</h1><ul><li>Alice</li><li>Bob</li></ul>",
"size": 48
},
"timestamp": 1234567890
}Template Syntax:
- Variable substitution:
{{variableName}} - Dot notation:
{{user.name}} - For loops:
{{#for item in items}}...{{/for}} - Automatic XSS protection via HTML escaping
HTTP Request → RouterServlet → Content-Type Check → ProcessorRegistry
↓
┌───────────────────────────────────────┴───────────────────────────────┐
↓ ↓ ↓ ↓ ↓
FormDataProcessor JsonDataProcessor FileUploadProcessor ScriptProcessor TemplateProcessor
(urlencoded forms) (JSON payloads) (file uploads) (JavaScript) (HTML templates)
↓ ↓ ↓ ↓ ↓
ProcessorResponse ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ←
↓
HTTP Response (JSON)
- Strategy Pattern: Request processors implement a common interface
- Registry Pattern: Central registry for processor discovery and routing
- Builder Pattern: ProcessorResponse construction
- Singleton Pattern: ProcessorRegistry and PropertiesUtil
- Template Method: Placeholder resolution in configuration
- RouterServlet: Central servlet handling all HTTP requests
- ProcessorRegistry: Manages and routes to appropriate processors
- RequestProcessor: Interface for content-type specific processing
- ScriptProcessor: JavaScript execution engine with performance monitoring
- TemplateProcessor: HTML template rendering with variable substitution
- TemplateEngine: Custom template parser and renderer
- PropertiesUtil: YAML configuration loader with environment variable support
- JsonUtil: JSON serialization/deserialization wrapper
The embedded Tomcat server uses the following defaults (configurable in application.yml):
- Max Threads: 200 (concurrent requests)
- Min Spare Threads: 10
- Accept Count: 100 (queue size)
- Connection Timeout: 20000 ms
- Jakarta EE Servlet API (via Embedded Tomcat 10.1.20)
- Apache Tomcat Embedded (web server)
- Gson 2.10.1 (JSON processing)
- SnakeYAML 2.2 (YAML configuration)
- Mozilla Rhino 1.7.15 (JavaScript engine for server-side execution)
- Java 17
- Maven (build tool with Shade plugin for executable JAR)
The application returns appropriate HTTP status codes:
200 OK- Successful processing400 Bad Request- Malformed data (invalid JSON, empty body, script errors, etc.)404 Not Found- Unknown endpoint408 Request Timeout- Script execution timeout exceeded413 Payload Too Large- File size or memory limit exceeded415 Unsupported Media Type- No processor for Content-Type500 Internal Server Error- Processing exception
Error responses follow this format:
{
"error": "Error type",
"message": "Detailed error message",
"status": 400,
"timestamp": 1234567890
}- Implement the
RequestProcessorinterface:
public class XmlDataProcessor implements RequestProcessor {
@Override
public boolean supports(String contentType) {
return contentType != null && contentType.startsWith("application/xml");
}
@Override
public ProcessorResponse process(HttpServletRequest request)
throws IOException, ServletException {
// Process XML data
return ProcessorResponse.builder()
.statusCode(200)
.body(responseJson)
.build();
}
@Override
public String getContentType() {
return "application/xml";
}
}- Register in
RouterServlet.init():
registry.register(new XmlDataProcessor());- Add route in
RouterServlet.doPost():
case "/api/xml":
handleProcessorRequest(request, response);
break;This project is open source and available under the MIT License.