Người dùng hiện nay mong đợi các ứng dụng linh động, nhanh chóng có thể truy cập được từ
trang web. Loạt bài này cho bạn thấy cách phát triển các ứng dụng web theo hướng sự kiện bằng
cách sử dụng các kỹ thuật Reverse Ajax. Phần 1 đã giới thiệu Reverse Ajax, polling, streaming,
Comet, long-polling. Bạn đã tìm hiểu vì sao Comet sử dụng HTTP long-polling là cách tốt nhất
để thực hiện Reverse Ajax, khi hiện nay tất cả các trình duyệt đều hỗ trợ nó. Phần 2 đã mô tả
cách thực hiện Reverse Ajax bằng cách sử dụng WebSockets. Các ví dụ mã giúp minh họa các
ràng buộc WebSockets, FlashSocket bên phía máy chủ, các dịch vụ phạm vi-yêu cầu, và cách để
tạm dừng các yêu cầu long-lived.
11 trang |
Chia sẻ: lylyngoc | Lượt xem: 1541 | Lượt tải: 1
Bạn đang xem nội dung tài liệu Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Reverse Ajax, Phần 3: Các máy chủ Web và Socket.IO
Giới thiệu
Người dùng hiện nay mong đợi các ứng dụng linh động, nhanh chóng có thể truy cập được từ
trang web. Loạt bài này cho bạn thấy cách phát triển các ứng dụng web theo hướng sự kiện bằng
cách sử dụng các kỹ thuật Reverse Ajax. Phần 1 đã giới thiệu Reverse Ajax, polling, streaming,
Comet, long-polling. Bạn đã tìm hiểu vì sao Comet sử dụng HTTP long-polling là cách tốt nhất
để thực hiện Reverse Ajax, khi hiện nay tất cả các trình duyệt đều hỗ trợ nó. Phần 2 đã mô tả
cách thực hiện Reverse Ajax bằng cách sử dụng WebSockets. Các ví dụ mã giúp minh họa các
ràng buộc WebSockets, FlashSocket bên phía máy chủ, các dịch vụ phạm vi-yêu cầu, và cách để
tạm dừng các yêu cầu long-lived.
Trong bài này, hãy đi chi tiết về cách sử dụng Comet và WebSockets trong ứng dụng web của
bạn với các API và các web container khác nhau (Servlet 3.0 và Continuations của Jetty). Hãy
tìm hiểu cách sử dụng Comet và WebSockets một cách trong suốt bằng cách sử dụng các thư
viện trừu tượng hóa, chẳng hạn như Socket.IO. Socket.IO sử dụng tính năng nhận dạng để quyết
định xem kết nối đó sẽ được thiết lập với WebSocket, AJAX long-polling, Flash và v.v hay
không.
Bạn có thể tải về mã nguồn được sử dụng trong bài này.
Điều kiện tiên quyết
Tốt nhất bạn nên biết trước về JavaScript và Java. Các ví dụ trong bài này đã được xây dựng
bằng cách sử dụng Google Guice, một framework tích hợp phụ thuộc (dependency injection
framework) được viết bằng Java. Để làm theo cùng với bài này, bạn nên hiểu rõ các khái niệm về
dependency injection framework, chẳng hạn như Guice, Spring hoặc Pico.
Để chạy được các ví dụ trong bài này, bạn cần có phiên bản mới nhất của Maven và JDK (xem
phần Tài nguyên).
Về đầu trang
Các giải pháp máy chủ cho Comet và WebSocket
Ở Phần 1, bạn đã tìm hiểu về phương pháp Comet (long-polling hay streaming) có khả năng yêu
cầu máy chủ có thể tạm dừng một request và tiếp tục hay hoàn thành nó sau một thời gian nắm
giữ. Phần 2 đã mô tả cách mà máy chủ cần sử dụng tính năng I/O non-blocking như thế nào để
xử lý nhiều kết nối và chúng chỉ sử dụng các luồng để phục vụ các yêu cầu (mô hình luồng cho
mỗi yêu cầu). Bạn cũng đã biết rằng việc sử dụng WebSocket phụ thuộc vào máy chủ và không
phải tất cả các máy chủ đều hỗ trợ WebSockets.
Phần này sẽ cho bạn thấy cách sử dụng Comet và WebSockets, nếu có, trên các máy chủ web
Jetty, Tomcat và Grizzly. Mã nguồn được cung cấp cùng với bài này có một ứng dụng web chat
(trò chuyện trực tuyến) là ví dụ mẫu cho Jetty và Tomcat. Phần này cũng thảo luận về các API
hỗ trợ cho các máy chủ ứng dụng sau: Jboss, Glassfish và WebSphere.
Jetty
Jetty là một máy chủ web hỗ trợ đặc tả Java Servlet 3.0, WebSockets và nhiều đặc tả tích hợp
khác. Jetty là:
Mạnh mẽ và linh hoạt.
Dễ dàng nhúng.
Hỗ trợ các máy chủ ảo, phân cụm session và rất nhiều tính năng có thể được cấu hình dễ
dàng thông qua mã Java hoặc XML.
Được dùng cho dịch vụ lưu trữ của Google App Engine.
Dự án Jetty được quản lý bởi Quỹ Eclipse.
Kể từ phiên bản 6, Jetty bao gồm một API không đồng bộ được gọi là Jetty Continuations, cho
phép một yêu cầu được tạm dừng và có thể tiếp tục lại sau đó. Bảng 1 cho thấy bản đồ về đặc tả
và API hỗ trợ với các họ phiên bản Jetty chính.
Bảng 1. Các phiên bản Jetty và khả năng hỗ trợ
Các sự hỗ trợ Jetty 6Jetty 7Jetty 8
Non-blocking I/O X X X
Servlet 2.5 X X X
Servlet 3.0 X X Jetty Continuations (Comet)X X X
WebSockets X X
Để thực hiện Reverse Ajax với Comet, bạn có thể sử dụng các API Continuation của Jetty, như
thể hiện trong Liệt kê 1:
Liệt kê 1. API Continuation của Jetty cho Comet
// Pausing a request from a servlet's method (get, post, ...):
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Continuation continuation = ContinuationSupport.getContinuation(req);
// optionally set a timeout to avoid suspending requests for too long
continuation.setTimeout(0);
// suspend the request
continuation.suspend();
// then hold a reference for future usage from another thread
continuations.offer(continuation);
}
// Then, from another thread which wants to send an event to the client:
while (!continuations.isEmpty()) {
Continuation continuation = continuations.poll();
HttpServletResponse response =
(HttpServletResponse) continuation.getServletResponse();
// write to the response
continuation.complete();
}
Ứng dụng web hoàn chỉnh có trong mã nguồn đi kèm với bài này. Jetty Continuations được đóng
gói trong một tệp JAR. Bạn phải đặt tệp JAR này trong thư mục WEB-INF/lib của ứng dụng web
của mình để có thể sử dụng các tính năng Comet của Jetty. Continuations của Jetty sẽ làm việc
trên Jetty 6, 7 và 8.
Bắt đầu với Jetty 7, bạn cũng có quyền truy cập vào tính năng WebSockets. Đặt tệp JAR
WebSocket của Jetty vào thư mục WEB-INF/lib của ứng dụng web của bạn để có quyền truy cập
vào API WebSocket của Jetty, như trong Liệt kê 2:
Liệt kê 2. API WebSocket của Jetty
// Implement the doWebSocketConnect and returns an implementation of
// WebSocket:
public final class ReverseAjaxServlet extends WebSocketServlet {
@Override
protected WebSocket doWebSocketConnect(HttpServletRequest request,
String protocol) {
return [...]
}
}
// Sample implementation of WebSocket:
class Endpoint implements WebSocket {
Outbound outbound;
public void onConnect(Outbound outbound) {
this.outbound = outbound;
}
public void onMessage(byte opcode, String data) {
outbound.sendMessage("Echo: " + data);
if("close".equals(data))
outbound.disconnect();
}
public void onFragment(boolean more, byte opcode,
byte[] data, int offset, int length) {
}
public void onMessage(byte opcode, byte[] data,
int offset, int length) {
onMessage(opcode, new String(data, offset, length));
}
public void onDisconnect() {
outbound = null;
}
}
Trong thư mục jetty-websocket củamã nguồn có một ví dụ chat mẫu để trình bày cách sử dụng
API WebSocket của Jetty.
Tomcat
Có lẽ Tomcat là máy chủ web nổi tiếng nhất. Nó đã được sử dụng trong nhiều năm và đã được
tích hợp như là một web container vào các phiên bản đầu tiên của máy chủ ứng dụng Jboss.
Ngoài ra, Tomcat còn được dùng cho việc thực thi tham khảo đặc tả servlet. Nó đã được loại bỏ
khỏi phiên bản API servlet 2.5 khi mọi người bắt đầu xem xét các lựa chọn thay thế dựa trên I/O
non-blocking (như Jetty). Bảng 2 cho thấy API và các đặc tả được hỗ trợ cho hai họ phiên bản
Tomcat mới nhất.
Bảng 2. Sự hỗ trợ của Tomcat
Các sự hỗ trợ Tomcat 6 Tomcat 7
Non-blocking I/O X X
Servlet 2.5 X X
Servlet 3.0 X Advanced I/O (Comet)X X
WebSockets
Như thể hiện trong Bảng 2, Tomcat không hỗ trợ WebSockets; nó có một bản tương đương với
Continuations của Jetty được gọi là I/O nâng cao (Advanced I/O) để hỗ trợ Comet. I/O nâng cao
là một trình bao bọc mức thấp xung quanh NIO mạnh hơn một API tốt để làm cho việc sử dụng
Comet dễ dàng hơn. Nó có ít tài liệu và chỉ có một vài ví dụ mô tả cách sử dụng API này. Liệt kê
3 cho thấy một ví dụ về servlet được sử dụng để treo và tiếp tục lại các yêu cầu trong một ứng
dụng web chat. Bạn có thể tìm thấy ứng dụng web hoàn chỉnh trong mã nguồn đi kèm với bài
này.
Liệt kê 3. API của Tomcat cho Comet
public final class ChatServlet extends HttpServlet
implements CometProcessor {
private final BlockingQueue events =
new LinkedBlockingQueue();
public void event(CometEvent evt)
throws IOException, ServletException {
HttpServletRequest request = evt.getHttpServletRequest();
String user =
(String) request.getSession().getAttribute("user");
switch (evt.getEventType()) {
case BEGIN: {
if ("GET".equals(request.getMethod())) {
evt.setTimeout(Integer.MAX_VALUE);
events.offer(evt);
} else {
String message = request.getParameter("message");
if ("/disconnect".equals(message)) {
broadcast(user + " disconnected");
request.getSession().removeAttribute("user");
events.remove(evt);
} else if (message != null) {
broadcast("[" + user + "]" + message);
}
evt.close();
}
}
}
}
void broadcast(String message) throws IOException {
Queue q = new LinkedList();
events.drainTo(q);
while (!q.isEmpty()) {
CometEvent event = q.poll();
HttpServletResponse resp = event.getHttpServletResponse();
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("text/html");
resp.getWriter().write(message);
event.close();
}
}
}
Trong Tomcat, một servlet không đồng bộ phải thực hiện một CometProcessor. Đối với các
servlet không đồng bộ, Tomcat không gọi các phương thức HTTP tiêu chuẩn (doGet, doPost và
v.v). Thay vào đó, nó gửi một sự kiện đến phương thức event(CometdEvent). Khi yêu cầu đầu
tiên gửi đến, ví dụ sẽ kiểm tra để xem liệu nó có là một GET để treo nó không; evt.close()
không được gọi. Nếu nó là một POST, có nghĩa là người dùng đã gửi một thông báo, do đó, nó
được broadcast (phát hàng loạt) đến CometEvent khác và evt.close() được gọi để hoàn thành
yêu cầu đăng thông báo. Về phía máy khách, việc broadcast sẽ thực hiện tất cả các yêu cầu long-
polling để hoàn thành thông báo đã gửi và một yêu cầu long-polling khác được gửi ngay để nhận
được các sự kiện tiếp theo.
Grizzly và Glassfish
Grizzly không phải là web container, nhưng hơn một framework NIO giúp các nhà phát triển xây
dựng các ứng dụng có khả năng co giãn. Nó được phát triển như là một phần trong dự án
Glassfish, nhưng nó cũng có thể được sử dụng độc lập hoặc nhúng vào hệ thống khác. Grizzly
cung cấp các thành phần như một máy chủ HTTP/HTTPS và các thành phần cho Bayeux
Protocol, Servlet, HTTPService OSGi và Comet, trong số những thứ khác. Grizzly hỗ trợ
WebSockets và được sử dụng trong Glassfish để cung cấp Comet và hỗ trợ WebSocket.
Glassfish, Application Server (Máy chủ ứng dụng) của Oracle, là cách thực hiện tham khảo của
các đặc tả J2EE 6. Glassfish là một gói hoàn chỉnh, như WebSphere và Jboss, khi sử dụng
Grizzly để hỗ trợ NIO, WebSocket và Comet. Kiến trúc mô đun của nó, dựa trên OSGI, giúp nó
linh hoạt trong việc thay đổi các thành phần. Bảng 3 cho thấy Glassfish hỗ trợ Comet và
WebSockets.
Bảng 3. Sự hỗ trợ của Glassfish
Các sự hỗ trợ Glassfish 2Glassfish 3
Non-blocking I/OX X
Servlet 2.5 X X
Servlet 3.0 X Comet X X
WebSockets X
Cách sử dụng Grizzly cũng rất quan trọng, khi người ta dự định sử dụng nó theo cách nhúng
hoặc trực tiếp từ mã Java. Nó được sử dụng rộng rãi như một framework để hỗ trợ Comet và
WebSockets, mà nó có thể được nhúng trong một ứng dụng lớn hơn, ví dụ như Glassfish, cung
cấp các khả năng triển khai web và API đặc tả Servlet.
Xem phần Tài nguyên để biết các liên kết đến các ví dụ của WebSockets và Comet trong Grizzly
hoặc Glassfish. Vì Glassfish sử dụng Grizzly, nên các ví dụ sẽ làm việc trên cả hai. API
WebSocket cũng rất giống như một API trong Jetty, nhưng API của Comet phức tạp hơn.
Jboss
Jboss là một máy chủ ứng dụng được xây dựng dựa trên Tomcat. Nó đã hỗ trợ Comet và NIO kể
từ phiên bản 5. Jboss 7 vẫn còn đang phát triển, nhưng được đưa vào trong Bảng 4 dưới đây.
Bảng 4. Sự hỗ trợ của Jboss
Các sự hỗ trợ Jboss 5Jboss 6 Jboss 7
Non-blocking I/OX X X
Servlet 2.5 X X X
Servlet 3.0 X X Comet X X X
WebSockets
WebSphere
WebSphere là một máy chủ ứng dụng của IBM. Phiên bản 8 của WebSphere đã hỗ trợ của API
Servlet 3 (có chứa API không đồng bộ tiêu chuẩn hóa cho Comet) (xem phần Tài nguyên để đọc
thông báo).
Bảng 5. Sự hỗ trợ của WebSphere
Các sự hỗ trợ WebSphere 8
Non-blocking I/OX
Servlet 2.5 X
Servlet 3.0 X
Comet X
WebSockets
Về đầu trang
Thế còn các API chung thì như thế nào?
Mỗi máy chủ đưa ra API nguyên gốc của riêng mình cho Comet và WebSocket. Như bạn có thể
đoán là việc viết một ứng dụng web di động có thể khó khăn. Đặc tả Servlet 3.0 gồm có các
phương thức bổ sung để treo và tiếp tục lại một yêu cầu sau đó, cho phép tất cả các web
container hỗ trợ Đặc tả Servlet 3.0 đều hỗ trợ các yêu cầu Comet long-polling.
Nhóm phát triển Jetty cung cấp một thư viện được gọi là Continuation của Jetty, độc lập với Jetty
container. Thư viện Continuation của Jetty đủ thông minh để phát hiện ra container hoặc đặc tả
có sẵn. Nếu bạn đang chạy trên một máy chủ Jetty, API của Jetty nguyên gốc sẽ được sử dụng.
Nếu bạn đang chạy trên một container hỗ trợ Đặc tả Servlet3.0, API chung (common API) này sẽ
được sử dụng. Nếu không, việc thực hiện không có khả năng co giãn sẽ được sử dụng.
Về WebSockets, vẫn chưa có tiêu chuẩn nào trong Java và do đó bạn cần sử dụng API container
của nhà cung cấp trong ứng dụng web của mình nếu bạn muốn sử dụng WebSockets.
Bảng 6 tóm tắt các công nghệ được các máy chủ khác nhau hỗ trợ.
Bảng 6. Các công nghệ được các máy chủ hỗ trợ
Container Comet WebSocket
Jetty 6 Jetty Continuations Không có
Jetty 7 Servlet 3.0 Jetty Continuations
Native Jetty API (API Jetty nguyên
gốc)
Jetty 8 Servlet 3.0 Jetty Continuations Native Jetty API
Tomcat 6 Advanced I/O (Vào/ra nâng cao) Không có
Tomcat 7
Servlet 3.0
Advanced I/O
Jetty Continuations
Không có
Glassfish 2 Native Grizzly API (API Grizzly nguyên gốc) Không có
Glassfish 3
Servlet 3.0
Native Grizzly API
Jetty Continuations
Native Grizzly API
Jboss 5 Native Jboss API (API Jboss nguyên gốc) Không có
Jboss 6
Servlet 3.0
Native Jboss API
Jetty Continuations
Không có
Jboss 7
Servlet 3.0
Native Jboss API
Jetty Continuations
Không có
WebSphere
8
Servlet 3.0
Jetty Continuations Không có
Không có giải pháp rõ ràng nào cho WebSockets ngoại trừ việc sử dụng API container. Như với
Comet, mỗi container hỗ trợ Đặc tả Servlet 3.0 đều hỗ trợ Comet. Ở đây lợi thế của Jetty
Continuations là cung cấp hỗ trợ Comet trên tất cả các container này. Vì vậy, một số thư viện
Reverse Ajax (được tiếp tục thảo luận dưới đây và trong phần tiếp theo của loạt bài này) đang sử
dụng Jetty Continuations API phía máy chủ.
API Jetty Continuation được hiển thị trong ví dụ mẫu trong bài này. Đặc tả Servlet 3.0 đã được
mô tả và được sử dụng trong hai ví dụ Comet trong Phần 1 của loạt bài này.
Về đầu trang
Các thư viện trừu tượng hóa
Khi xem xét tất cả các API chủ yếu (Servlet 3.0 và Jetty Continuations), cộng với tất cả sự hỗ trợ
bên phía máy chủ và hai cách chủ yếu để thực hiện Reverse Ajax bên phía máy khách (Comet và
WebSocket) sẽ thấy thật khó để viết JavaScript và mã Java riêng của bạn để nối chúng lại với
nhau. Bạn cũng phải tính đến các yếu tố như thời gian chờ timeout, các lỗi kết nối, tin báo nhận,
chỉ dẫn, giữ dữ liệu trong bộ đệm và v.v.
Phần còn lại của bài này sẽ cho bạn thấy Socket.IO hoạt động như thế nào. Phần 4 của loạt bài
này sẽ tìm hiểu Atmosphere và CometD. Tất cả ba thư viện này đều là nguồn mở và tất cả chúng
đều hỗ trợ Comet và WebSocket trên nhiều máy chủ.
Socket.IO
Socket.IO là một thư viện máy khách JavaScript cung cấp một API riêng, tương tự như
WebSocket, để kết nối đến một máy chủ từ xa để gửi và nhận thông báo không đồng bộ. Bằng
cách cung cấp một API chung, Socket.IO hỗ trợ một số kiểu truyền tải: WebSocket, Flash
Sockets, long-polling, streaming, forever Iframes (Các khung nội tuyến vô hạn) và JSONP
polling. Socket.IO phát hiện ra các khả năng của trình duyệt và cố gắng lựa chọn kiểu truyền tải
tốt nhất có thể. Thư viện Socket.IO tương thích với hầu hết tất cả các trình duyệt (bao gồm cả
các trình duyệt cũ, chẳng hạn như IE 5.5) cũng như các trình duyệt di động. Nó cũng có các tính
năng như các nhịp hoạt động (heartbeat), thời gian chờ, ngắt kết nối và xử lý lỗi.
Trang web Socket.IO (xem phần Tài nguyên) mô tả chi tiết thư viện này hoạt động ra sao và
trình duyệt và kỹ thuật Reverse Ajax nào được sử dụng. Về cơ bản, Socket.IO sử dụng một giao
thức truyền thông cho phép thư viện máy khách truyền thông với một điểm cuối ở phía máy chủ,
mà điểm này có thể hiểu được giao thức Socket.IO. Đầu tiên người ta phát triển Socket.IO cho
Node JS, một công cụ JavaScript được sử dụng để xây dựng các máy chủ nhanh hơn. Nhưng giờ
đây, nhiều dự án đã mang đến sự hỗ trợ cho các ngôn ngữ khác, bao gồm cả Java.
Liệt kê 4 cho thấy một ví dụ về sử dụng thư viện JavaScript Socket.IO bên phía máy khách.
Trang web Socket.IO có tài liệu hướng dẫn và các ví dụ.
Liệt kê 4. Sử dụng thư viện máy khách Socket.IO
var socket = new io.Socket(document.domain, {
resource: 'chat'
});
socket.on('connect', function() {
// Socket.IO is connected
});
socket.on('disconnect', function(disconnectReason, errorMessage) {
// Socket.IO disconnected
});
socket.on('message', function(mtype, data, error) {
// The server sent an event
});
// Now that the handlers are defined, establish the connection:
socket.connect();
Để sử dụng thư viện JavaScript Socket.IO, bạn sẽ cần một thành phần Java tương ứng được gọi
là Socket.IO Java (xem phần Tài nguyên). Ban đầu dự án này được nhóm công tác Apache Wave
khởi động để đem đến sự hỗ trợ Reverse Ajax cho Wave trước khi WebSockets được hỗ trợ.
Socket.IO Java được chia làm hai nhánh và được duy trì bởi Ovea (một công ty chuyên về phát
triển web theo hướng sự kiện) và sau đó bị bỏ rơi. Việc phát triển tầng sau để hỗ trợ thư viện
máy khách Socket.IO có phức tạp là do có nhiều kiểu truyền tải. Phần 4 của loạt bài này sẽ chỉ ra
vì sao việc hỗ trợ nhiều kiểu truyền tải trong một thư viện máy khách là không cần thiết để đạt
được khả năng co giãn và hỗ trợ trình duyệt tốt hơn, do chỉ cần long-polling và WebSockets là
đủ. Khi chưa hỗ trợ WebSockets, Socket.IO đã là một lựa chọn tốt.
Socket.IO Java sử dụng API Jetty Continuation để treo và tiếp tục lại các yêu cầu. Nó sử dụng
API WebSockets của Jetty nguyên gốc để hỗ trợ WebSockets. Bạn có thể xác định máy chủ nào
sẽ làm việc đúng với một ứng dụng web khi sử dụng Socket.IO Java.
Liệt kê 5, dưới đây, cho thấy một ví dụ về cách sử dụng Socket.IO trên máy chủ. Bạn phải định
nghĩa một SocketIOServlet mở rộng của servlet và thực hiện một phương thức để trả về một
loại đại diện điểm cuối. API này rất giống với API WebSockets. Ưu điểm là ở chỗ API này được
sử dụng bên phía máy chủ, độc lập với việc truyền tải được chọn bên phía máy khách. Socket.IO
chuyển dịch tất cả các kiểu truyền tải tới API tương tự bên phía máy chủ.
Liệt kê 5. Thư viện Socket.IO Java được sử dụng cho ví dụ mẫu chat - servlet
public final class ChatServlet extends SocketIOServlet {
private final BlockingQueue endpoints =
new LinkedBlockingQueue();
@Override
protected SocketIOInbound doSocketIOConnect
(HttpServletRequest request) {
String user =
(String) request.getSession().getAttribute("user");
return user == null ? null : new Endpoint(this, user, request);
}
void broadcast(String data) {
for (Endpoint endpoint : endpoints) {
endpoint.send(data);
}
}
void add(Endpoint endpoint) {
endpoints.offer(endpoint);
}
void remove(Endpoint endpoint) {
endpoints.remove(endpoint);
}
}
Liệt kê 6 cho thấy cách trả về điểm cuối.
Liệt kê 6. Sử dụng thư viện Socket.IO Java cho ví dụ mẫu chat - điểm cuối
class Endpoint implements SocketIOInbound {
[...]
private SocketIOOutbound outbound;
[...]
@Override
public void onConnect(SocketIOOutbound outbound) {
this.outbound = outbound;
servlet.add(this);
servlet.broadcast(user + " connected");
}
@Override
public void onDisconnect(DisconnectReason reason,
String errorMessage) {
outbound = null;
request.getSession().removeAttribute("user");
servlet.remove(this);
servlet.broadcast(user + " disconnected");
}
@Override
public void onMessage(int messageType, String message) {
if ("/disconnect".equals(message)) {
outbound.close();
} else