Vấn đề thực tế: Java Desktop app mà không ai chịu cài
Có lần khách hàng giao cho team mình một bài toán khó: họ có một ứng dụng Java Desktop cũ (Swing GUI), chạy tốt trên Windows nhưng toàn bộ đội ngũ 50 người muốn dùng từ browser — không cần cài JRE, không cần IT support từng máy. Rewrite lại thành web app? Mất 6 tháng và không có budget. Dùng TeamViewer hay AnyDesk? Quá phức tạp khi scale lên 50 concurrent users.
Đó là lần đầu mình gặp Webswing — và nó giải quyết đúng cái vấn đề đó trong vài giờ.
Application Virtualization là gì? Webswing fit ở đâu?
Trước khi đi vào cài đặt, hãy nhìn qua bức tranh tổng thể một chút. Application Virtualization — ảo hóa ứng dụng — về cơ bản là: app chạy trên server, client chỉ nhận giao diện được stream về. Bạn thao tác được, nhìn thấy được, nhưng không có gì cài trên máy cả.
Webswing áp dụng ý tưởng đó riêng cho Java: toàn bộ app Swing/JavaFX chạy trên server, output render ra được stream xuống browser dưới dạng canvas HTML5. Mỗi click chuột, mỗi phím gõ — browser capture rồi gửi lên server, server xử lý và đẩy frame mới về. Điều hay nhất: app Java gốc không cần thay đổi gì cả. Nó hoàn toàn không biết mình đang chạy trong browser.
Điểm khác biệt so với các giải pháp như Citrix hay RDS:
- Không cần Windows Server license: Webswing chạy ngon trên Linux
- Không cần client plugin: Chỉ cần modern browser (Chrome, Firefox, Edge)
- Native HTTP/WebSocket: Dễ reverse proxy qua nginx, tích hợp được vào existing infrastructure
- Per-session isolation: Mỗi user chạy một JVM riêng, không ảnh hưởng lẫn nhau
Trong homelab của mình — Proxmox VE quản lý 12 VM và container, playground để test mọi thứ trước khi đưa lên production — Webswing chạy trong một LXC container Ubuntu 22.04. Setup xong, test ổn rồi mới nhân lên cho khách hàng.
Cài đặt và cấu hình Webswing
Chuẩn bị môi trường
Webswing cần Java 11+ để chạy. Mình dùng Ubuntu 22.04:
sudo apt update
sudo apt install -y openjdk-17-jdk wget unzip
java -version
# openjdk version "17.0.x"
Download Webswing từ trang chính thức. Bản Community Edition miễn phí cho mục đích phi thương mại:
# Kiểm tra version mới nhất tại webswing.org trước khi download
wget https://webswing.org/download/webswing-23.2.zip -O webswing.zip
unzip webswing.zip -d /opt/
mv /opt/webswing-* /opt/webswing
Chạy Webswing lần đầu
cd /opt/webswing
./webswing.sh
Webswing khởi động trên port 8080. Truy cập http://localhost:8080 sẽ thấy Admin Console với các demo app được bundle sẵn như SwingSet3, JFreeChart. Default credentials: admin / admin — đổi ngay nếu không muốn bị bẽ mặt trong production.
Cấu hình ứng dụng Java của bạn
Giả sử mình có một JAR file tên inventory-app.jar. Thêm entry vào file /opt/webswing/webswing.config:
{
"path": "inventory",
"name": "Inventory Management",
"mainClass": "com.company.inventory.MainApp",
"classPathEntries": [
"/opt/apps/inventory-app.jar",
"/opt/apps/lib/*.jar"
],
"vmArgs": "-Xmx512m -Dfile.encoding=UTF-8",
"maxClients": 10,
"isolatedFs": true
}
Sau khi lưu config, restart Webswing. App sẽ xuất hiện tại http://localhost:8080/inventory.
Reverse proxy với nginx
Production không bao giờ expose port 8080 trực tiếp. Setup nginx như sau:
server {
listen 443 ssl;
server_name apps.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/apps.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/apps.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
# WebSocket support — bắt buộc cho Webswing
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Tăng timeout cho long-running sessions
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Header Upgrade và Connection: upgrade là bắt buộc — thiếu cái này WebSocket không kết nối được và app sẽ không render. Mình đã mắc lỗi này lần đầu và mất gần 30 phút debug.
Chạy Webswing như systemd service
sudo tee /etc/systemd/system/webswing.service << 'EOF'
[Unit]
Description=Webswing Application Server
After=network.target
[Service]
Type=simple
User=webswing
WorkingDirectory=/opt/webswing
ExecStart=/opt/webswing/webswing.sh
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Tạo user riêng — không bao giờ chạy service bằng root
sudo useradd -r -s /bin/false webswing
sudo chown -R webswing:webswing /opt/webswing
sudo systemctl daemon-reload
sudo systemctl enable --now webswing
sudo systemctl status webswing
Những thứ cần tính trước khi production
RAM planning
Mỗi user session spawn một JVM riêng. Nếu app cần 256MB heap, 10 concurrent users tốn 2.5GB+ RAM chưa tính OS overhead. Đặt maxClients phù hợp với RAM server và monitor bằng jstat hoặc Grafana nếu có sẵn stack giám sát.
Filesystem isolation
Bật isolatedFs: true để mỗi session có thư mục home riêng, tránh user đọc file của nhau. Quan trọng nếu app có tính năng save/open file — mình đã thấy một case dữ liệu bị lẫn lộn giữa các session vì quên bật cái này.
Clipboard và file transfer
Copy-paste text và upload/download file qua browser dialog hoạt động được, nhưng cần test kỹ với app cụ thể. Không phải tất cả Swing component đều hoạt động hoàn hảo — đặc biệt là custom renderer và một số dialog native OS.
Giới hạn Community Edition
Miễn phí nhưng kéo theo vài ràng buộc thực tế: concurrent sessions bị giới hạn, không có SSO/LDAP integration, không có session recording hay cluster mode. Dùng thử hoặc nội bộ nhỏ thì ổn; cần scale lớn hoặc audit trail thì bản thương mại là tất yếu.
Mình đã chạy thử với một app Swing cũ từ 2008 — không sửa một dòng code nào, chỉ cần đúng JDK version là chạy được trên browser. Latency trên LAN gần như không cảm nhận được; qua internet với khoảng 20ms RTT vẫn dùng thoải mái cho tác vụ nghiệp vụ thông thường.
Kết luận
Có option rewrite lại thành web app native? Cứ làm vậy — về lâu dài vẫn clean hơn. Nhưng không phải lúc nào cũng có 6 tháng và budget để rewrite. Khi bị kẹt giữa hai thái cực đó, Webswing là lối thoát ít đau đớn nhất mình từng thử.
Dưới 2 giờ từ zero đến URL chạy được. Không một dòng code Java nào bị đụng vào. So với viết lại app từ đầu hoặc mua Citrix license, đó là con số đáng suy nghĩ cho bất kỳ legacy Java app nào đang nằm chờ được “web hóa”.

