diff --git a/springChatRoom/README.md b/springChatRoom/README.md
index 9ce895bfc..aeee9eb09 100644
--- a/springChatRoom/README.md
+++ b/springChatRoom/README.md
@@ -53,12 +53,12 @@ java -jar target/springChatRoom-0.0.1-SNAPSHOT.jar
## Todo
- Feature
+ - show user(online/offline) list
- private msg (1 to 1)
- setup group, group chat
- @ "notification"
- msg push (?)
- "read" 已讀 feature
- - show user list
- save history msg
- broadcast msg
- push msg to users (offer API)
@@ -67,16 +67,21 @@ java -jar target/springChatRoom-0.0.1-SNAPSHOT.jar
## Knowledge
- WebSocket
- - Server can send info to client (bi-directon communicartion, TCP protocol)
- - 3 ways handshake
- - server, client side can send/receive msg, and terminate connection
- - long connection between server and client, no need to check with client every time
- - more efficient when communicating (header is shorter)
+ - Server can send info to client (bi-directon communicartion, TCP protocol)
+ - 3 ways handshake
+ - server, client side can send/receive msg, and terminate connection
+ - long connection between server and client, no need to check with client every time
+ - more efficient when communicating (header is shorter)
- Use case of WebSocket
- - chat room
- - online game
- - geo location app
- - stock market app
- - co-editing system (e.g. google doc ?)
- - app needs near real-time update/sync, data transmit is not small
\ No newline at end of file
+ - chat room
+ - online game
+ - geo location app
+ - stock market app
+ - co-editing system (e.g. google doc ?)
+ - app needs near real-time update/sync, data transmit is not small
+
+## Reference
+
+- redisTemplate API cmd
+ - https://ost.51cto.com/posts/2333
\ No newline at end of file
diff --git a/springChatRoom/doc/ref.md b/springChatRoom/doc/ref.md
index 78584b470..190605fce 100644
--- a/springChatRoom/doc/ref.md
+++ b/springChatRoom/doc/ref.md
@@ -1,21 +1,29 @@
## Ref
- Article
- - https://blog.csdn.net/qqxx6661/article/details/98883166
- - https://github.com/yennanliu/springboot-websocket-demo
+ - https://blog.csdn.net/qqxx6661/article/details/98883166
+ - https://github.com/yennanliu/springboot-websocket-demo
- - https://ithelp.ithome.com.tw/articles/10197142
- - https://ithelp.ithome.com.tw/articles/10197191
- - https://www.baeldung.com/websockets-spring
- - https://blog.csdn.net/wwd0501/article/details/54582912
- - https://www.slideshare.net/wenhsiaoyi/java-api-for-websocket
- - https://www.syscom.com.tw/ePaper_New_Content.aspx?id=368&EPID=194&TableName=sgEPArticle
- - https://tw511.com/a/01/16646.html
+ - https://ithelp.ithome.com.tw/articles/10197142
+ - https://ithelp.ithome.com.tw/articles/10197191
+ - https://www.baeldung.com/websockets-spring
+ - https://blog.csdn.net/wwd0501/article/details/54582912
+ - https://www.slideshare.net/wenhsiaoyi/java-api-for-websocket
+ - https://www.syscom.com.tw/ePaper_New_Content.aspx?id=368&EPID=194&TableName=sgEPArticle
+ - https://tw511.com/a/01/16646.html
- Code
- - https://spring.io/guides/gs/messaging-stomp-websocket/
- - https://morosedog.gitlab.io/springboot-20190416-springboot28/
+ - https://spring.io/guides/gs/messaging-stomp-websocket/
+ - https://morosedog.gitlab.io/springboot-20190416-springboot28/
- Video
- - https://youtu.be/r4fdPmZuzmY?si=qrmRTm09Bo53Ina1
- - https://youtu.be/RbCFeeePJoM?si=DF6mES5XApvSkhXw
- - https://youtu.be/es_fTKyfI4w?si=vVhEz6Us-zrcerTR
\ No newline at end of file
+ - https://youtu.be/r4fdPmZuzmY?si=qrmRTm09Bo53Ina1
+ - https://youtu.be/RbCFeeePJoM?si=DF6mES5XApvSkhXw
+ - https://youtu.be/es_fTKyfI4w?si=vVhEz6Us-zrcerTR
+
+- Advanced
+ - Chat with specific user
+ - https://www.baeldung.com/spring-websockets-send-message-to-user
+ - https://github.com/eugenp/tutorials/tree/master/spring-security-modules/spring-security-web-sockets - code
+ - https://juejin.cn/post/6844903717636947981
+
+ - https://www.jb51.net/article/257091.htm
\ No newline at end of file
diff --git a/springChatRoom/pom.xml b/springChatRoom/pom.xml
index 1d351b556..33c78d1c6 100644
--- a/springChatRoom/pom.xml
+++ b/springChatRoom/pom.xml
@@ -51,7 +51,13 @@
1.2.79
-
+
+ junit
+ junit
+ test
+
+
+
diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/bean/OnlineUser.java b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/OnlineUser.java
new file mode 100644
index 000000000..f64dcda35
--- /dev/null
+++ b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/OnlineUser.java
@@ -0,0 +1,26 @@
+package com.yen.springChatRoom.bean;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class OnlineUser {
+
+ public OnlineUser(){
+ }
+
+ public OnlineUser(List users){
+ this.users = users;
+ }
+
+ private List users;
+
+ public List getUsers() {
+ return users;
+ }
+
+ public void setUsers(List users) {
+ this.users = users;
+ }
+
+}
diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java b/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java
index c9a86a13f..27a94ffce 100644
--- a/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java
+++ b/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java
@@ -12,6 +12,9 @@
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+
+import java.util.Set;
@Controller
public class ChatController {
@@ -25,6 +28,8 @@ public class ChatController {
@Value("${redis.channel.userStatus}")
private String userStatus;
+ final String onlineUserKey = "websocket.onlineUsers";
+
// TODO : check difference ? RedisTemplate VS RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
@@ -58,7 +63,6 @@ public void sendMessage(@Payload ChatMessage chatMessage){
}
}
-
@MessageMapping("/chat.addUser")
public void addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {
@@ -67,28 +71,12 @@ public void addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor
headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
redisTemplate.opsForSet().add(onlineUsers, chatMessage.getSender());
redisTemplate.convertAndSend(userStatus, JsonUtil.parseObjToJson(chatMessage));
+
+ // show online user
+
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}
}
-// @MessageMapping("/chat.addUser")
-// @SendTo("/topic/public")
-// public ChatMessage addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {
-//
-// LOGGER.info("User added in Chatroom:" + chatMessage.getSender());
-// // add username in web socket session
-// headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
-// return chatMessage;
-//
-// // TODO : update with below
-//// try {
-//// headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
-////// redisTemplate.opsForSet().add(onlineUsers, chatMessage.getSender());
-////// redisTemplate.convertAndSend(userStatus, JsonUtil.parseObjToJson(chatMessage));
-//// } catch (Exception e) {
-//// LOGGER.error(e.getMessage(), e);
-//// }
-// }
-
}
diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/controller/UserController.java b/springChatRoom/src/main/java/com/yen/springChatRoom/controller/UserController.java
new file mode 100644
index 000000000..dc2fa3864
--- /dev/null
+++ b/springChatRoom/src/main/java/com/yen/springChatRoom/controller/UserController.java
@@ -0,0 +1,53 @@
+package com.yen.springChatRoom.controller;
+
+import com.yen.springChatRoom.bean.OnlineUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+ @Value("${redis.channel.msgToAll}")
+ private String msgToAll;
+
+ @Value("${redis.set.onlineUsers}")
+ private String onlineUsers;
+
+ @Value("${redis.channel.userStatus}")
+ private String userStatus;
+
+ final String onlineUserKey = "websocket.onlineUsers";
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChatController.class);
+
+ @GetMapping("/online_user")
+ public List getOnlineUser(){
+
+ Set resultSet = redisTemplate.opsForSet().members(onlineUserKey);
+ System.out.println("(getOnlineUser) resultSet = " + resultSet);
+ // TODO : optimize below
+ OnlineUser onlineUser = new OnlineUser();
+ List users = new ArrayList<>();
+ resultSet.forEach(x -> {
+ users.add(x);
+ });
+ onlineUser.setUsers(users);
+ return onlineUser.getUsers();
+ }
+
+}
diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java b/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java
index 47dfee335..72b714d91 100644
--- a/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java
+++ b/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java
@@ -79,21 +79,4 @@ public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
}
}
-// @EventListener
-// public void handleWebSocketDisConnectListener(SessionDisconnectEvent event){
-//
-// StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
-// String username = (String) headerAccessor.getSessionAttributes().get("username");
-//
-// if (username != null){
-//
-// LOGGER.info("User disconnected : " + username);
-// ChatMessage chatMessage = new ChatMessage();
-// chatMessage.setType(ChatMessage.MessageType.LEAVE);
-// chatMessage.setSender(username);
-//
-// messagingTemplate.convertAndSend("/topic/public", chatMessage);
-// }
-// }
-
}
diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java b/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java
index ec1e6bdda..266f8f5ae 100644
--- a/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java
+++ b/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java
@@ -49,7 +49,6 @@ public void onMessage(Message message, byte[] bytes) {
return;
}
-
if (msgToAll.equals(topic)) {
LOGGER.info("Send message to all users:" + rawMsg);
ChatMessage chatMessage = JsonUtil.parseJsonToObj(rawMsg, ChatMessage.class);
diff --git a/springChatRoom/src/main/resources/static/index.html b/springChatRoom/src/main/resources/static/index.html
index e2974a07e..74dc8d61e 100644
--- a/springChatRoom/src/main/resources/static/index.html
+++ b/springChatRoom/src/main/resources/static/index.html
@@ -50,6 +50,12 @@ Spring WebSocket Chat Demo
+
+
+
diff --git a/springChatRoom/src/main/resources/static/js/main.js b/springChatRoom/src/main/resources/static/js/main.js
index a6234e8aa..f995f4201 100644
--- a/springChatRoom/src/main/resources/static/js/main.js
+++ b/springChatRoom/src/main/resources/static/js/main.js
@@ -119,5 +119,34 @@ function getAvatarColor(messageSender) {
return colors[index];
}
+// online user
+function fetchUserList() {
+ fetch('/user/online_user') // Replace with the actual endpoint URL
+ .then(response => response.json())
+ .then(data => {
+ updateOnlineUsers(data);
+ })
+ .catch(error => {
+ console.error('Error fetching user list: ', error);
+ });
+}
+
+function updateOnlineUsers(users) {
+ const userList = document.getElementById('userList');
+ userList.innerHTML = ''; // Clear the list first
+
+ users.forEach(user => {
+ const listItem = document.createElement('li');
+ listItem.textContent = user;
+ userList.appendChild(listItem);
+ });
+}
+
+// Call the fetchUserList function to initially populate the user list
+fetchUserList();
+
+// You can also periodically update the user list using a timer or other events
+setInterval(fetchUserList, 5000); // Update every 5 seconds (adjust as needed)
+
usernameForm.addEventListener('submit', connect, true)
messageForm.addEventListener('submit', sendMessage, true)
\ No newline at end of file
diff --git a/springChatRoom/src/test/java/com/yen/springChatRoom/controller/ChatControllerTest.java b/springChatRoom/src/test/java/com/yen/springChatRoom/controller/ChatControllerTest.java
new file mode 100644
index 000000000..cf8120efa
--- /dev/null
+++ b/springChatRoom/src/test/java/com/yen/springChatRoom/controller/ChatControllerTest.java
@@ -0,0 +1,32 @@
+package com.yen.springChatRoom.controller;
+
+import org.junit.jupiter.api.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+class ChatControllerTest {
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ @Test
+ public void testQueryRedis(){
+
+ final String onlineUserKey = "websocket.onlineUsers";
+ // https://ost.51cto.com/posts/2333
+ Set resultSet = redisTemplate.opsForSet().members(onlineUserKey);
+ System.out.println("resultSet:" + resultSet);
+
+ }
+
+}
\ No newline at end of file