public void sendMessageOnChannel( HttpServletRequest request, final String message, final String channel, final String... excludeUsers) { // get the application scoped model, and copy the list of subscribers, so that the // long running task of publishing doesnt interfere with new logins // final AsyncContext publisherAsyncCtx = request.startAsync(); ServletContext appScope = request.getServletContext(); @SuppressWarnings("unchecked") final Map<String, List<Subscriber>> clients = (Map<String, List<Subscriber>>) appScope.getAttribute(Keys.CLIENTS); final List<Subscriber> subscribers = new ArrayList<Subscriber>(clients.get(channel)); // here is the logic for publishing - it will be passed to the container // for execution sometime in the future // Runnable r = new Runnable() { // @Override // public void run() { System.out.println("updating games channel for " + subscribers.size() + " subscribers..."); long start = System.currentTimeMillis(); // keep a list of failed subscribers so we can remove them at // the end List<Subscriber> toRemove = new ArrayList<Subscriber>(); for (Subscriber s : subscribers) { if (null != excludeUsers && excludeUsers.length > 0 && Arrays.asList(excludeUsers).contains(s.getUser().getUserName())) { continue; } synchronized (s) { AsyncContext aCtx = s.getaCtx(); try { aCtx.getResponse().getOutputStream().print(message); aCtx.complete(); } catch (Exception e) { System.err.println( "failed to send to client - removing from list of subscribers on this channel"); toRemove.add(s); } } } // remove the failed subscribers from the model in app scope, // not our copy of them synchronized (clients) { clients.get(channel).removeAll(toRemove); } // log success long ms = System.currentTimeMillis() - start; String ok = "finished updating games channel " + channel + " in " + ms + " ms."; System.out.println(ok); // publisherAsyncCtx.complete(); // we are done, the connection can // // be closed now // } // }; // // // start the async processing (using a pool controlled by the container) // publisherAsyncCtx.start(r); }