diff --git a/springChatRoom/pom.xml b/springChatRoom/pom.xml index 33c78d1c6..7d19061fd 100644 --- a/springChatRoom/pom.xml +++ b/springChatRoom/pom.xml @@ -57,6 +57,12 @@ test + + + org.projectlombok + lombok + + diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/model/ChatMessage.java b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/ChatMessage.java similarity index 95% rename from springChatRoom/src/main/java/com/yen/springChatRoom/model/ChatMessage.java rename to springChatRoom/src/main/java/com/yen/springChatRoom/bean/ChatMessage.java index e792fd2cb..4271b2cfb 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/model/ChatMessage.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/ChatMessage.java @@ -1,4 +1,4 @@ -package com.yen.springChatRoom.model; +package com.yen.springChatRoom.bean; public class ChatMessage { private MessageType type; diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/bean/Message.java b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/Message.java new file mode 100644 index 000000000..88c40ec09 --- /dev/null +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/bean/Message.java @@ -0,0 +1,17 @@ +package com.yen.springChatRoom.bean; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class Message { + + private String sender; + private String content; + private String type; +} diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/config/WebSocketConfig.java b/springChatRoom/src/main/java/com/yen/springChatRoom/config/WebSocketConfig.java index 5b156b685..6e3d7e487 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/config/WebSocketConfig.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/config/WebSocketConfig.java @@ -1,6 +1,8 @@ package com.yen.springChatRoom.config; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; @@ -28,7 +30,16 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { public void configureMessageBroker(MessageBrokerRegistry registry) { //WebSocketMessageBrokerConfigurer.super.configureMessageBroker(registry); - registry.setApplicationDestinationPrefixes("/app"); - registry.enableSimpleBroker("/topic"); + registry.setApplicationDestinationPrefixes("/app"); // (client -> server) + registry.enableSimpleBroker("/topic", "/private"); // (server -> client) } + + // +// @Bean +// public SimpMessagingTemplate messagingTemplate() { +// +// //return new SimpMessagingTemplate(/* your message broker relay, e.g., "/topic" */); +// return new SimpMessagingTemplate("/private/"); +// } + } 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 27a94ffce..aa6c0bacf 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/controller/ChatController.java @@ -1,21 +1,23 @@ package com.yen.springChatRoom.controller; -import com.yen.springChatRoom.model.ChatMessage; +import com.yen.springChatRoom.bean.Message; +import com.yen.springChatRoom.bean.ChatMessage; import com.yen.springChatRoom.util.JsonUtil; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.messaging.handler.annotation.DestinationVariable; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; -import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; 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; +import org.springframework.web.bind.annotation.RequestMapping; +@Slf4j @Controller public class ChatController { @@ -28,6 +30,9 @@ public class ChatController { @Value("${redis.channel.userStatus}") private String userStatus; + @Value("${redis.channel.private}") + private String privateChannel; + final String onlineUserKey = "websocket.onlineUsers"; // TODO : check difference ? RedisTemplate VS RedisTemplate @@ -35,6 +40,9 @@ public class ChatController { private RedisTemplate redisTemplate; //private RedisTemplate redisTemplate; + @Autowired + private SimpMessagingTemplate simpMessagingTemplate; + private static final Logger LOGGER = LoggerFactory.getLogger(ChatController.class); /** @@ -56,6 +64,9 @@ public class ChatController { @MessageMapping("/chat.sendMessage") public void sendMessage(@Payload ChatMessage chatMessage){ try{ + + // test : save msg to redis + redisTemplate.opsForSet().add(msgToAll, JsonUtil.parseObjToJson(chatMessage)); //redisTemplate.convertAndSend(msgToAll, JsonUtil.parseObjToJson(chatMessage))); redisTemplate.convertAndSend(msgToAll, JsonUtil.parseObjToJson(chatMessage)); }catch (Exception e){ @@ -79,4 +90,17 @@ public void addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor } } + // TODO : check @DestinationVariable ? + @RequestMapping("/app/private/{username}") + public void handlePrivateMessage(@DestinationVariable String username, Message message){ + + log.info("handlePrivateMessage : username = " + username + " message = " + message); + // save to redis + + // redisTemplate.convertAndSend(userStatus, JsonUtil.parseObjToJson(chatMessage)); + redisTemplate.opsForSet().add(privateChannel + "." + username, JsonUtil.parseObjToJson(message)); + + simpMessagingTemplate.convertAndSendToUser(username, "/topic/private", message); + } + } 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 72b714d91..5c33d781d 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/listener/WebSocketEventListener.java @@ -1,7 +1,7 @@ package com.yen.springChatRoom.listener; import com.yen.springChatRoom.controller.ChatController; -import com.yen.springChatRoom.model.ChatMessage; +import com.yen.springChatRoom.bean.ChatMessage; import com.yen.springChatRoom.util.JsonUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 266f8f5ae..eb46219dd 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/redis/RedisListenerHandle.java @@ -1,6 +1,6 @@ package com.yen.springChatRoom.redis; -import com.yen.springChatRoom.model.ChatMessage; +import com.yen.springChatRoom.bean.ChatMessage; import com.yen.springChatRoom.service.ChatService; import com.yen.springChatRoom.util.JsonUtil; import org.slf4j.Logger; diff --git a/springChatRoom/src/main/java/com/yen/springChatRoom/service/ChatService.java b/springChatRoom/src/main/java/com/yen/springChatRoom/service/ChatService.java index 105f8a736..b692ed38d 100644 --- a/springChatRoom/src/main/java/com/yen/springChatRoom/service/ChatService.java +++ b/springChatRoom/src/main/java/com/yen/springChatRoom/service/ChatService.java @@ -1,13 +1,12 @@ package com.yen.springChatRoom.service; import com.yen.springChatRoom.controller.ChatController; -import com.yen.springChatRoom.model.ChatMessage; +import com.yen.springChatRoom.bean.ChatMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.simp.SimpMessageSendingOperations; -import org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler; import org.springframework.stereotype.Service; @Service diff --git a/springChatRoom/src/main/resources/application.properties b/springChatRoom/src/main/resources/application.properties index 6f0ab9d97..5d2930acd 100644 --- a/springChatRoom/src/main/resources/application.properties +++ b/springChatRoom/src/main/resources/application.properties @@ -18,3 +18,5 @@ server.port=8080 redis.set.onlineUsers = websocket.onlineUsers redis.channel.msgToAll = websocket.msgToAll redis.channel.userStatus = websocket.userStatus +redis.channel.private = websocket.privateMsg + diff --git a/springChatRoom/src/main/resources/static/js/main.js b/springChatRoom/src/main/resources/static/js/main.js index 71d72a5ac..069c605a8 100644 --- a/springChatRoom/src/main/resources/static/js/main.js +++ b/springChatRoom/src/main/resources/static/js/main.js @@ -37,9 +37,13 @@ function connect(event) { function onConnected() { + // Subscribe to the Public Topic stompClient.subscribe('/topic/public', onMessageReceived); + // Subscribe to the "/private" destination // TODO : make it general + //stompClient.subscribe('/private/user123', onPrivateMessageReceived); + // Tell your username to the server stompClient.send("/app/chat.addUser", {}, @@ -158,7 +162,6 @@ function updateOnlineUsers(users) { // Append the username span to the list item listItem.appendChild(usernameSpan); - // Create a "Chat" button const chatButton = document.createElement('button'); chatButton.textContent = 'Chat'; @@ -167,7 +170,6 @@ function updateOnlineUsers(users) { // Append the "Chat" button to the list item listItem.appendChild(chatButton); - // Append the list item to the user list userList.appendChild(listItem); }); @@ -189,6 +191,8 @@ function startChat(username) { // Function to send a message from the popup window popupWindow.sendMessage = function() { + + console.log(">>> popupWindow.sendMessage") const messageInput = popupWindow.document.getElementById('messageInput'); const chatMessages = popupWindow.document.getElementById('chatMessages'); @@ -197,15 +201,38 @@ function startChat(username) { // Customize the way messages are displayed in the popup window chatMessages.innerHTML += '

You: ' + message + '

'; + // TODO: Fetch and display chat history + //fetchChatHistory(username, chatMessages); + + // TODO : implement below in BE // Add your logic to send the message to the other user // Example: stompClient.send('/app/private/' + username, {}, JSON.stringify({ sender: 'You', content: message, type: 'CHAT' })); + // send msg to BE + //stompClient.subscribe('/app/private/' + username, onPrivateMessageReceived); + stompClient.subscribe('/app/private/' + username); + console.log(">>> send msg to /app/private/" + username + ", message = " + message); + stompClient.send('/app/private/' + username, {}, JSON.stringify({ sender: 'You', content: message, type: 'CHAT' })); + // Clear the input field messageInput.value = ''; } }; } +// Function to fetch and display chat history +function fetchChatHistory(username, chatMessages) { + fetch('/app/chat/history/' + username) + .then(response => response.json()) + .then(history => { + history.forEach(message => { + chatMessages.innerHTML += '

' + message.sender + ': ' + message.content + '

'; + }); + }) + .catch(error => { + console.error('Error fetching chat history: ', error); + }); +} // Call the fetchUserList function to initially populate the user list