You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openmeetings.apache.org by so...@apache.org on 2019/02/11 10:40:14 UTC

[openmeetings] branch master updated: [OPENMEETINGS-1990] timestamps are now client based

This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 8380e4d  [OPENMEETINGS-1990] timestamps are now client based
8380e4d is described below

commit 8380e4da65a9d38bfe55aa4f413b43318df0aed0
Author: Maxim Solodovnik <so...@gmail.com>
AuthorDate: Mon Feb 11 17:31:35 2019 +0700

    [OPENMEETINGS-1990] timestamps are now client based
---
 .../core/util/ChatWebSocketHelper.java             | 147 +++++++++++++++++++++
 .../openmeetings/core/util/WebSocketHelper.java    | 145 +++++---------------
 .../{WsMessageUser.java => WsMessageChat2All.java} |  21 +--
 ...{WsMessageUser.java => WsMessageChat2User.java} |  15 +--
 .../openmeetings/core/util/ws/WsMessageUser.java   |  11 +-
 .../openmeetings/db/manager/IClientManager.java    |   1 +
 .../apache/openmeetings/web/app/ClientManager.java |   1 +
 .../web/room/wb/WbWebSocketHelper.java             |   4 +-
 .../apache/openmeetings/web/user/chat/Chat.java    |  12 +-
 .../openmeetings/web/user/chat/ChatForm.java       |  16 +--
 .../openmeetings/web/user/chat/ChatPanel.java      |   2 +-
 .../openmeetings/web/user/chat/ChatToolbar.java    |   4 +-
 12 files changed, 221 insertions(+), 158 deletions(-)

diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
new file mode 100644
index 0000000..7a2951a
--- /dev/null
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ChatWebSocketHelper.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License") +  you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openmeetings.core.util;
+
+import java.util.List;
+import java.util.function.BiConsumer;
+
+import org.apache.commons.lang3.time.FastDateFormat;
+import org.apache.openmeetings.IApplication;
+import org.apache.openmeetings.core.util.ws.WsMessageChat;
+import org.apache.openmeetings.core.util.ws.WsMessageChat2All;
+import org.apache.openmeetings.core.util.ws.WsMessageChat2User;
+import org.apache.openmeetings.db.entity.basic.ChatMessage;
+import org.apache.openmeetings.db.entity.room.Room.Right;
+import org.apache.openmeetings.db.entity.user.User;
+import org.apache.openmeetings.db.manager.IClientManager;
+import org.apache.openmeetings.db.util.FormatHelper;
+import org.apache.openmeetings.util.ws.IClusterWsMessage;
+
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
+
+public class ChatWebSocketHelper extends WebSocketHelper {
+	public static final String ID_TAB_PREFIX = "chatTab-";
+	public static final String ID_ALL = ID_TAB_PREFIX + "all";
+	public static final String ID_ROOM_PREFIX = ID_TAB_PREFIX + "r";
+	public static final String ID_USER_PREFIX = ID_TAB_PREFIX + "u";
+
+	private static JSONObject setScope(JSONObject o, ChatMessage m, long curUserId) {
+		String scope, scopeName = null;
+		if (m.getToUser() != null) {
+			User u = curUserId == m.getToUser().getId() ? m.getFromUser() : m.getToUser();
+			scope = ID_USER_PREFIX + u.getId();
+			scopeName = u.getDisplayName();
+		} else if (m.getToRoom() != null) {
+			scope = ID_ROOM_PREFIX + m.getToRoom().getId();
+			o.put("needModeration", m.isNeedModeration());
+		} else {
+			scope = ID_ALL;
+		}
+		return o.put("scope", scope).put("scopeName", scopeName);
+	}
+
+	public static JSONObject getMessage(User curUser, List<ChatMessage> list, BiConsumer<JSONObject, User> uFmt) {
+		JSONArray arr = new JSONArray();
+		for (ChatMessage m : list) {
+			String smsg = m.getMessage();
+			smsg = smsg == null ? smsg : " " + smsg.replaceAll("&nbsp;", " ") + " ";
+			JSONObject from = new JSONObject()
+					.put("id", m.getFromUser().getId())
+					.put("displayName", m.getFromName())
+					.put("name", m.getFromUser().getDisplayName());
+			if (uFmt != null) {
+				uFmt.accept(from, m.getFromUser());
+			}
+			arr.put(setDates(setScope(new JSONObject(), m, curUser.getId())
+					.put("id", m.getId())
+					.put("message", smsg)
+					.put("from", from)
+					.put("actions", curUser.getId() == m.getFromUser().getId() ? "short" : "full")
+				, m, curUser, true));
+		}
+		return new JSONObject()
+			.put("type", "chat")
+			.put("msg", arr);
+	}
+
+	public static void send(IClusterWsMessage msg) {
+		if (msg instanceof WsMessageChat) {
+			if (msg instanceof WsMessageChat2User) {
+				WsMessageChat2User m = (WsMessageChat2User)msg;
+				sendUser(m.getUserId(), m.getChatMessage(), m.getMsg(), false);
+			} else if (msg instanceof WsMessageChat2All) {
+				WsMessageChat2All m = (WsMessageChat2All)msg;
+				sendAll(m.getChatMessage(), m.getMsg(), false);
+			} else if (msg instanceof WsMessageChat) {
+				WsMessageChat m = (WsMessageChat)msg;
+				sendRoom(m.getChatMessage(), m.getMsg(), false);
+			}
+		} else {
+			WebSocketHelper.send(msg);
+		}
+	}
+
+	public static void sendRoom(ChatMessage m, JSONObject msg) {
+		sendRoom(m, msg, true);
+	}
+
+	private static JSONObject setDates(JSONObject o, ChatMessage m, User u, boolean immediate) {
+		final FastDateFormat fullFmt = FormatHelper.getDateTimeFormat(u);
+		final FastDateFormat dateFmt = FormatHelper.getDateFormat(u);
+		final FastDateFormat timeFmt = FormatHelper.getTimeFormat(u);
+		JSONObject obj = immediate ? o : o.getJSONArray("msg").getJSONObject(0);
+		obj.put("sent", fullFmt.format(m.getSent()))
+				.put("date", dateFmt.format(m.getSent()))
+				.put("time", timeFmt.format(m.getSent()));
+		return o;
+	}
+
+	private static void sendRoom(ChatMessage m, JSONObject msg, boolean publish) {
+		if (publish) {
+			publish(new WsMessageChat(m, msg));
+		}
+		sendRoom(m.getToRoom().getId(), msg
+				, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.moderator))
+				, (o, c) -> setDates(o, m, c.getUser(), false));
+	}
+
+	public static void sendUser(final Long userId, ChatMessage m, JSONObject msg) {
+		sendUser(userId, m, msg, true);
+	}
+
+	private static void sendUser(final Long userId, ChatMessage m, JSONObject msg, boolean publish) {
+		if (publish) {
+			publish(new WsMessageChat2User(userId, m, msg));
+		}
+		sendUser(userId, msg, (o, c) -> setDates(o, m, c.getUser(), false), false);
+	}
+
+	public static void sendAll(ChatMessage m, JSONObject msg) {
+		sendAll(m, msg, true);
+	}
+
+	private static void sendAll(ChatMessage m, JSONObject msg, boolean publish) {
+		if (publish) {
+			publish(new WsMessageChat2All(m, msg));
+		}
+		send(a -> ((IApplication)a).getBean(IClientManager.class).list()
+				, (t, c) -> doSend(t, c, msg, (o, cm) -> setDates(o, m, c.getUser(), false), "all"), null);
+	}
+}
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
index 5f96e09..985619f 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/WebSocketHelper.java
@@ -22,30 +22,24 @@ import static org.apache.openmeetings.util.OpenmeetingsVariables.getWicketApplic
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.openmeetings.IApplication;
 import org.apache.openmeetings.core.util.ws.WsMessageAll;
-import org.apache.openmeetings.core.util.ws.WsMessageChat;
 import org.apache.openmeetings.core.util.ws.WsMessageRoom;
 import org.apache.openmeetings.core.util.ws.WsMessageRoomMsg;
 import org.apache.openmeetings.core.util.ws.WsMessageRoomOthers;
 import org.apache.openmeetings.core.util.ws.WsMessageUser;
-import org.apache.openmeetings.db.entity.basic.ChatMessage;
 import org.apache.openmeetings.db.entity.basic.Client;
 import org.apache.openmeetings.db.entity.basic.IWsClient;
-import org.apache.openmeetings.db.entity.room.Room.Right;
-import org.apache.openmeetings.db.entity.user.User;
 import org.apache.openmeetings.db.manager.IClientManager;
-import org.apache.openmeetings.db.util.FormatHelper;
 import org.apache.openmeetings.db.util.ws.RoomMessage;
 import org.apache.openmeetings.db.util.ws.TextRoomMessage;
+import org.apache.openmeetings.util.NullStringer;
 import org.apache.openmeetings.util.ws.IClusterWsMessage;
 import org.apache.wicket.Application;
 import org.apache.wicket.protocol.ws.WebSocketSettings;
@@ -56,60 +50,10 @@ import org.apache.wicket.protocol.ws.concurrent.Executor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.openjson.JSONArray;
 import com.github.openjson.JSONObject;
 
 public class WebSocketHelper {
 	private static final Logger log = LoggerFactory.getLogger(WebSocketHelper.class);
-	public static final String ID_TAB_PREFIX = "chatTab-";
-	public static final String ID_ALL = ID_TAB_PREFIX + "all";
-	public static final String ID_ROOM_PREFIX = ID_TAB_PREFIX + "r";
-	public static final String ID_USER_PREFIX = ID_TAB_PREFIX + "u";
-
-	private static JSONObject setScope(JSONObject o, ChatMessage m, long curUserId) {
-		String scope, scopeName = null;
-		if (m.getToUser() != null) {
-			User u = curUserId == m.getToUser().getId() ? m.getFromUser() : m.getToUser();
-			scope = ID_USER_PREFIX + u.getId();
-			scopeName = u.getDisplayName();
-		} else if (m.getToRoom() != null) {
-			scope = ID_ROOM_PREFIX + m.getToRoom().getId();
-			o.put("needModeration", m.isNeedModeration());
-		} else {
-			scope = ID_ALL;
-		}
-		return o.put("scope", scope).put("scopeName", scopeName);
-	}
-
-	public static JSONObject getMessage(User curUser, List<ChatMessage> list, BiConsumer<JSONObject, User> uFmt) {
-		JSONArray arr = new JSONArray();
-		final FastDateFormat fullFmt = FormatHelper.getDateTimeFormat(curUser);
-		final FastDateFormat dateFmt = FormatHelper.getDateFormat(curUser);
-		final FastDateFormat timeFmt = FormatHelper.getTimeFormat(curUser);
-		for (ChatMessage m : list) {
-			String smsg = m.getMessage();
-			smsg = smsg == null ? smsg : " " + smsg.replaceAll("&nbsp;", " ") + " ";
-			JSONObject from = new JSONObject()
-					.put("id", m.getFromUser().getId())
-					.put("displayName", m.getFromName())
-					.put("name", m.getFromUser().getDisplayName());
-			if (uFmt != null) {
-				uFmt.accept(from, m.getFromUser());
-			}
-			arr.put(setScope(new JSONObject(), m, curUser.getId())
-				.put("id", m.getId())
-				.put("message", smsg)
-				.put("from", from)
-				.put("actions", curUser.getId() == m.getFromUser().getId() ? "short" : "full")
-				.put("sent", fullFmt.format(m.getSent()))
-				.put("date", dateFmt.format(m.getSent()))
-				.put("time", timeFmt.format(m.getSent()))
-				);
-		}
-		return new JSONObject()
-			.put("type", "chat")
-			.put("msg", arr);
-	}
 
 	public static void sendClient(final IWsClient _c, byte[] b) {
 		if (_c != null) {
@@ -150,23 +94,20 @@ public class WebSocketHelper {
 		}
 	}
 
-	public static void send(IClusterWsMessage _m) {
-		if (_m instanceof WsMessageRoomMsg) {
-			sendRoom(((WsMessageRoomMsg)_m).getMsg(), false);
-		} else if (_m instanceof WsMessageRoomOthers) {
-			WsMessageRoomOthers m = (WsMessageRoomOthers)_m;
+	public static void send(IClusterWsMessage msg) {
+		if (msg instanceof WsMessageRoomMsg) {
+			sendRoom(((WsMessageRoomMsg)msg).getMsg(), false);
+		} else if (msg instanceof WsMessageRoomOthers) {
+			WsMessageRoomOthers m = (WsMessageRoomOthers)msg;
 			sendRoomOthers(m.getRoomId(), m.getUid(), m.getMsg(), false);
-		} else if (_m instanceof WsMessageRoom) {
-			WsMessageRoom m = (WsMessageRoom)_m;
+		} else if (msg instanceof WsMessageRoom) {
+			WsMessageRoom m = (WsMessageRoom)msg;
 			sendRoom(m.getRoomId(), m.getMsg(), false);
-		} else if (_m instanceof WsMessageChat) {
-			WsMessageChat m = (WsMessageChat)_m;
-			sendRoom(m.getChatMessage(), m.getMsg(), false);
-		} else if (_m instanceof WsMessageUser) {
-			WsMessageUser m = (WsMessageUser)_m;
-			sendUser(m.getUserId(), m.getMsg(), false);
-		} else if (_m instanceof WsMessageAll) {
-			sendAll(((WsMessageAll)_m).getMsg(), false);
+		} else if (msg instanceof WsMessageUser) {
+			WsMessageUser m = (WsMessageUser)msg;
+			sendUser(m.getUserId(), m.getMsg(), null, false);
+		} else if (msg instanceof WsMessageAll) {
+			sendAll(((WsMessageAll)msg).getMsg(), false);
 		}
 	}
 
@@ -215,34 +156,16 @@ public class WebSocketHelper {
 		sendRoom(roomId, m, c -> !uid.equals(c.getUid()), null);
 	}
 
-	public static void sendRoom(ChatMessage m, JSONObject msg) {
-		sendRoom(m, msg, true);
+	public static void sendUser(final Long userId, final JSONObject m) {
+		sendUser(userId, m, null, true);
 	}
 
-	private static void sendRoom(ChatMessage m, JSONObject msg, boolean publish) {
-		if (publish) {
-			publish(new WsMessageChat(m, msg));
-		}
-		sendRoom(m.getToRoom().getId(), msg
-				, c -> !m.isNeedModeration() || (m.isNeedModeration() && c.hasRight(Right.moderator))
-				, null);
-	}
-
-	public static void sendUser(final Long userId, final String m) {
-		sendUser(userId, m, true);
-	}
-
-	private static void sendUser(final Long userId, final String m, boolean publish) {
+	static void sendUser(final Long userId, final JSONObject m, BiFunction<JSONObject, Client, JSONObject> func, boolean publish) {
 		if (publish) {
 			publish(new WsMessageUser(userId, m));
 		}
-		send(a -> ((IApplication)a).getBean(IClientManager.class).listByUser(userId), (t, c) -> {
-			try {
-				t.sendMessage(m);
-			} catch (Exception e) {
-				log.error("Error while sending message to user", e);
-			}
-		}, null);
+		send(a -> ((IApplication)a).getBean(IClientManager.class).listByUser(userId)
+				, (t, c) -> doSend(t, c, m, func, "user"), null);
 	}
 
 	public static void sendAll(final String m) {
@@ -254,13 +177,7 @@ public class WebSocketHelper {
 			publish(new WsMessageAll(m));
 		}
 		log.debug("Sending WebSocket message: {}", m);
-		sendAll(c -> {
-			try {
-				c.sendMessage(m);
-			} catch (Exception e) {
-				log.error("Error while sending message to ALL", e);
-			}
-		});
+		sendAll(c -> doSend(c, m, "ALL"));
 	}
 
 	private static void sendAll(Consumer<IWebSocketConnection> sender) {
@@ -283,22 +200,28 @@ public class WebSocketHelper {
 		new Thread(() -> app.publishWsTopic(m)).start();
 	}
 
-	protected static void sendRoom(final Long roomId, final JSONObject m, Predicate<Client> check, BiFunction<JSONObject, Client, String> func) {
+	protected static void sendRoom(final Long roomId, final JSONObject m, Predicate<Client> check, BiFunction<JSONObject, Client, JSONObject> func) {
 		log.debug("Sending WebSocket message: {}", m);
-		sendRoom(roomId, (t, c) -> {
-			try {
-				t.sendMessage(func == null ? m.toString() : func.apply(m, c));
-			} catch (Exception e) {
-				log.error("Error while broadcasting message to room", e);
-			}
-		}, check);
+		sendRoom(roomId, (t, c) -> doSend(t, c, m, func, "room"), check);
+	}
+
+	static void doSend(IWebSocketConnection conn, Client c, JSONObject msg, BiFunction<JSONObject, Client, JSONObject> func, String suffix) {
+		doSend(conn, (func == null ? msg : func.apply(msg, c)).toString(new NullStringer()), suffix);
+	}
+
+	private static void doSend(IWebSocketConnection c, String msg, String suffix) {
+		try {
+			c.sendMessage(msg);
+		} catch (IOException e) {
+			log.error("Error while sending message to {}", suffix, e);
+		}
 	}
 
 	private static void sendRoom(final Long roomId, BiConsumer<IWebSocketConnection, Client> consumer, Predicate<Client> check) {
 		send(a -> ((IApplication)a).getBean(IClientManager.class).listByRoom(roomId), consumer, check);
 	}
 
-	private static void send(
+	static void send(
 			final Function<Application, Collection<Client>> func
 			, BiConsumer<IWebSocketConnection, Client> consumer
 			, Predicate<Client> check)
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2All.java
similarity index 72%
copy from openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
copy to openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2All.java
index 4b7b32b..cde069e 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2All.java
@@ -18,23 +18,14 @@
  */
 package org.apache.openmeetings.core.util.ws;
 
-import org.apache.openmeetings.util.ws.IClusterWsMessage;
+import org.apache.openmeetings.db.entity.basic.ChatMessage;
 
-public class WsMessageUser implements IClusterWsMessage {
-	private static final long serialVersionUID = 1L;
-	private final Long userId;
-	private final String msg;
-
-	public WsMessageUser(Long userId, String msg) {
-		this.userId = userId;
-		this.msg = msg;
-	}
+import com.github.openjson.JSONObject;
 
-	public Long getUserId() {
-		return userId;
-	}
+public class WsMessageChat2All extends WsMessageChat {
+	private static final long serialVersionUID = 1L;
 
-	public String getMsg() {
-		return msg;
+	public WsMessageChat2All(ChatMessage m, JSONObject msg) {
+		super(m, msg);
 	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2User.java
similarity index 80%
copy from openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
copy to openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2User.java
index 4b7b32b..1305ae0 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageChat2User.java
@@ -18,23 +18,20 @@
  */
 package org.apache.openmeetings.core.util.ws;
 
-import org.apache.openmeetings.util.ws.IClusterWsMessage;
+import org.apache.openmeetings.db.entity.basic.ChatMessage;
 
-public class WsMessageUser implements IClusterWsMessage {
+import com.github.openjson.JSONObject;
+
+public class WsMessageChat2User extends WsMessageChat {
 	private static final long serialVersionUID = 1L;
 	private final Long userId;
-	private final String msg;
 
-	public WsMessageUser(Long userId, String msg) {
+	public WsMessageChat2User(Long userId, ChatMessage m, JSONObject msg) {
+		super(m, msg);
 		this.userId = userId;
-		this.msg = msg;
 	}
 
 	public Long getUserId() {
 		return userId;
 	}
-
-	public String getMsg() {
-		return msg;
-	}
 }
diff --git a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
index 4b7b32b..0530693 100644
--- a/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
+++ b/openmeetings-core/src/main/java/org/apache/openmeetings/core/util/ws/WsMessageUser.java
@@ -18,23 +18,26 @@
  */
 package org.apache.openmeetings.core.util.ws;
 
+import org.apache.openmeetings.util.NullStringer;
 import org.apache.openmeetings.util.ws.IClusterWsMessage;
 
+import com.github.openjson.JSONObject;
+
 public class WsMessageUser implements IClusterWsMessage {
 	private static final long serialVersionUID = 1L;
 	private final Long userId;
 	private final String msg;
 
-	public WsMessageUser(Long userId, String msg) {
+	public WsMessageUser(Long userId, JSONObject msg) {
 		this.userId = userId;
-		this.msg = msg;
+		this.msg = msg.toString(new NullStringer());
 	}
 
 	public Long getUserId() {
 		return userId;
 	}
 
-	public String getMsg() {
-		return msg;
+	public JSONObject getMsg() {
+		return new JSONObject(msg);
 	}
 }
diff --git a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
index 17d3b22..274f1d5 100644
--- a/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
+++ b/openmeetings-db/src/main/java/org/apache/openmeetings/db/manager/IClientManager.java
@@ -28,6 +28,7 @@ public interface IClientManager {
 	Client get(String uid);
 	Client getBySid(String sid);
 	String uidBySid(String sid);
+	List<Client> list();
 	List<Client> listByRoom(Long roomId);
 	Collection<Client> listByUser(Long userId);
 	Client update(Client c);
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
index 3b69309..94dffd3 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/app/ClientManager.java
@@ -217,6 +217,7 @@ public class ClientManager implements IClientManager {
 		return isUserOnline;
 	}
 
+	@Override
 	public List<Client> list() {
 		return new ArrayList<>(map().values());
 	}
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
index 2707e6d..552d7e2 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/wb/WbWebSocketHelper.java
@@ -167,7 +167,7 @@ public class WbWebSocketHelper extends WebSocketHelper {
 				, new JSONObject().put("type", "wb")
 				, null
 				, (o, c) -> o.put("func", WbAction.createObj.name())
-							.put("param", getObjWbJson(wbId, patchUrls(fi, c, _f))).toString(new NullStringer()));
+							.put("param", getObjWbJson(wbId, patchUrls(fi, c, _f))));
 	}
 
 	private static void sendWb(Long roomId, WbAction meth, JSONObject obj, Predicate<Client> check) {
@@ -176,7 +176,7 @@ public class WbWebSocketHelper extends WebSocketHelper {
 				, new JSONObject().put("type", "wb")
 				, check
 				, (o, c) -> o.put("func", meth.name())
-							.put("param", obj).toString(new NullStringer())
+							.put("param", obj)
 			);
 	}
 }
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
index f3c7209..76b355d 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/Chat.java
@@ -18,8 +18,8 @@
  */
 package org.apache.openmeetings.web.user.chat;
 
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ALL;
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ROOM_PREFIX;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ALL;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ROOM_PREFIX;
 import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DASHBOARD_SHOW_CHAT;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.room.RoomPanel.isModerator;
@@ -34,7 +34,7 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
-import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.core.util.ChatWebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ChatDao;
 import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -81,12 +81,12 @@ public class Chat extends Panel {
 					if (m.isNeedModeration() && isModerator(cm, getUserId(), roomId)) {
 						m.setNeedModeration(false);
 						chatDao.update(m);
-						WebSocketHelper.sendRoom(m, getMessage(Arrays.asList(m)).put("mode",  "accept"));
+						ChatWebSocketHelper.sendRoom(m, getMessage(Arrays.asList(m)).put("mode",  "accept"));
 					} else {
 						log.error("It seems like we are being hacked!!!!");
 					}
 				} else if (type != null && type.indexOf("typing") > -1) {
-					WebSocketHelper.sendRoom(roomId
+					ChatWebSocketHelper.sendRoom(roomId
 							, new JSONObject().put(PARAM_TYPE, "typing")
 									.put("active", type.indexOf("start") > -1)
 									.put("uid", getUid()));
@@ -135,7 +135,7 @@ public class Chat extends Panel {
 	}
 
 	public static JSONObject getMessage(User curUser, List<ChatMessage> list) {
-		return WebSocketHelper.getMessage(curUser, list, (o, u) -> o.put("img", getUrl(RequestCycle.get(), u)));
+		return ChatWebSocketHelper.getMessage(curUser, list, (o, u) -> o.put("img", getUrl(RequestCycle.get(), u)));
 	}
 
 	public CharSequence getReinit() {
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
index b289a47..2863283 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatForm.java
@@ -18,9 +18,9 @@
  */
 package org.apache.openmeetings.web.user.chat;
 
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ALL;
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ROOM_PREFIX;
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_USER_PREFIX;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ALL;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ROOM_PREFIX;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_USER_PREFIX;
 import static org.apache.openmeetings.web.app.WebSession.getUserId;
 import static org.apache.openmeetings.web.room.RoomPanel.isModerator;
 
@@ -29,7 +29,7 @@ import java.util.Date;
 import java.util.function.BooleanSupplier;
 import java.util.function.Predicate;
 
-import org.apache.openmeetings.core.util.WebSocketHelper;
+import org.apache.openmeetings.core.util.ChatWebSocketHelper;
 import org.apache.openmeetings.db.dao.basic.ChatDao;
 import org.apache.openmeetings.db.dao.room.RoomDao;
 import org.apache.openmeetings.db.dao.user.UserDao;
@@ -134,13 +134,13 @@ public class ChatForm extends Form<Void> {
 					chatDao.update(m);
 					JSONObject msg = getChat().getMessage(Arrays.asList(m));
 					if (m.getToRoom() != null) {
-						WebSocketHelper.sendRoom(m, msg);
+						ChatWebSocketHelper.sendRoom(m, msg);
 					} else if (m.getToUser() != null) {
-						WebSocketHelper.sendUser(getUserId(), msg.toString());
+						ChatWebSocketHelper.sendUser(getUserId(), m, msg);
 						msg = Chat.getMessage(m.getToUser(), Arrays.asList(m));
-						WebSocketHelper.sendUser(m.getToUser().getId(), msg.toString());
+						ChatWebSocketHelper.sendUser(m.getToUser().getId(), m, msg);
 					} else {
-						WebSocketHelper.sendAll(msg.toString());
+						ChatWebSocketHelper.sendAll(m, msg);
 					}
 					chatMessage.setDefaultModelObject("");
 					target.appendJavaScript("Chat.clean();");
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatPanel.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatPanel.java
index 98eecd5..6b3b2d1 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatPanel.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatPanel.java
@@ -18,7 +18,7 @@
  */
 package org.apache.openmeetings.web.user.chat;
 
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ROOM_PREFIX;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ROOM_PREFIX;
 
 import org.apache.openmeetings.db.entity.room.Room;
 import org.apache.openmeetings.db.entity.room.Room.RoomElement;
diff --git a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
index b87f1d8..644620e 100644
--- a/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
+++ b/openmeetings-web/src/main/java/org/apache/openmeetings/web/user/chat/ChatToolbar.java
@@ -19,7 +19,7 @@
 package org.apache.openmeetings.web.user.chat;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.openmeetings.core.util.WebSocketHelper.ID_ALL;
+import static org.apache.openmeetings.core.util.ChatWebSocketHelper.ID_ALL;
 import static org.apache.openmeetings.db.util.AuthLevelUtil.hasAdminLevel;
 import static org.apache.openmeetings.web.app.WebSession.getDateFormat;
 import static org.apache.openmeetings.web.app.WebSession.getRights;
@@ -205,7 +205,7 @@ public class ChatToolbar extends Panel implements IWysiwygToolbar {
 						return true;
 					}, u -> {
 						chatDao.deleteUser(u.getId());
-						WebSocketHelper.sendUser(u.getId(), cleanMsg(scope).toString());
+						WebSocketHelper.sendUser(u.getId(), cleanMsg(scope));
 						return true;
 					});
 			}