Skip to content

Commit

Permalink
chatroom-dev-003-code-optimize (#190)
Browse files Browse the repository at this point in the history
* use jdk 17, fix logger, remove no need code, format

* format all java code with google formatter
  • Loading branch information
yennanliu authored Nov 10, 2024
1 parent 1fa5013 commit a61c60e
Show file tree
Hide file tree
Showing 13 changed files with 292 additions and 331 deletions.
3 changes: 2 additions & 1 deletion springChatRoom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
<description>Demo project for Spring Boot</description>

<properties>
<java.version>11</java.version>
<!-- TODO : check if use JDK 11 or 17 -->
<java.version>17</java.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

// NOTE !!! DO NOT enable below, or will cause websocket connection error
//@ComponentScan(basePackages = "com.yen.springChatRoom.redis.RedisListenerBean") // https://blog.csdn.net/automal/article/details/111859409
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,51 @@
package com.yen.springChatRoom.bean;

public class ChatMessage {
private MessageType type;
private String content;
private String sender;

public MessageType getType() {
return type;
}

public void setType(MessageType type) {
this.type = type;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getSender() {
return sender;
}

public void setSender(String sender) {
this.sender = sender;
}

@Override
public String toString() {
return "ChatMessage{" +
"type=" + type +
", content='" + content + '\'' +
", sender='" + sender + '\'' +
'}';
}

public enum MessageType {
CHAT,
JOIN,
LEAVE
}

private MessageType type;
private String content;
private String sender;

public MessageType getType() {
return type;
}

public void setType(MessageType type) {
this.type = type;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getSender() {
return sender;
}

public void setSender(String sender) {
this.sender = sender;
}

@Override
public String toString() {
return "ChatMessage{"
+ "type="
+ type
+ ", content='"
+ content
+ '\''
+ ", sender='"
+ sender
+ '\''
+ '}';
}

public enum MessageType {
CHAT,
JOIN,
LEAVE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@ToString
public class Message {

private String sender;
private String content;
private String type;
private String sender;
private String content;
private String type;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@

public class OnlineUser {

private List<String> users;
private List<String> users;

public OnlineUser() {
}
public OnlineUser() {}

public OnlineUser(List<String> users) {
this.users = users;
}
public OnlineUser(List<String> users) {
this.users = users;
}

public List<String> getUsers() {
return users;
}

public void setUsers(List<String> users) {
this.users = users;
}
public List<String> getUsers() {
return users;
}

public void setUsers(List<String> users) {
this.users = users;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,41 @@
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

/**
* Stomp : spring implement WebSocket via Stomp
* <p>
* - https://blog.csdn.net/qq_21294185/article/details/130657375
* - https://blog.csdn.net/u013749113/article/details/131455579
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {

//WebSocketMessageBrokerConfigurer.super.registerStompEndpoints(registry);
registry.addEndpoint("ws").withSockJS(); // if browser not support websocket, use SockJS instead
}


@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {

//WebSocketMessageBrokerConfigurer.super.configureMessageBroker(registry);
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/");
// }
private final String APP_PRIFIX = "/app";
private final String TOPIC_PRIFIX = "/topic";
private final String PRIVATE_PRIFIX = "/private";

private final String WS_PATH = "ws";

/**
* Stomp : spring implement WebSocket via Stomp
*
* <p>- https://blog.csdn.net/qq_21294185/article/details/130657375 -
* https://blog.csdn.net/u013749113/article/details/131455579
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {

// WebSocketMessageBrokerConfigurer.super.registerStompEndpoints(registry);
registry
.addEndpoint(WS_PATH)
.withSockJS(); // if browser not support websocket, use SockJS instead
}

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {

// WebSocketMessageBrokerConfigurer.super.configureMessageBroker(registry);
registry.setApplicationDestinationPrefixes(APP_PRIFIX); // (client -> server)
registry.enableSimpleBroker(TOPIC_PRIFIX, PRIVATE_PRIFIX); // (server -> client)
}

//
// @Bean
// public SimpMessagingTemplate messagingTemplate() {
//
// //return new SimpMessagingTemplate(/* your message broker relay, e.g., "/topic" */);
// return new SimpMessagingTemplate("/private/");
// }

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.yen.springChatRoom.controller;

import com.yen.springChatRoom.bean.Message;
import com.yen.springChatRoom.bean.ChatMessage;
import com.yen.springChatRoom.bean.Message;
import com.yen.springChatRoom.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -10,97 +10,79 @@
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.simp.SimpMessageHeaderAccessor;
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.RequestMapping;

@Slf4j
@Controller
public class ChatController {

@Value("${redis.channel.msgToAll}")
private String msgToAll;

@Value("${redis.set.onlineUsers}")
private String onlineUsers;

@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<String, String>
@Autowired
private RedisTemplate<String, String> redisTemplate;
//private RedisTemplate redisTemplate;

@Autowired
private SimpMessagingTemplate simpMessagingTemplate;

private static final Logger LOGGER = LoggerFactory.getLogger(ChatController.class);

/**
* single mode : read msg from FE, and send to
* other users (@SendTo("/topic/public")) directly
*/
// @MessageMapping("/chat.sendMessage")
// @SendTo("/topic/public")
// public ChatMessage sendMessage(@Payload ChatMessage chatMessage){
//
// return chatMessage;
// }

/**
* cluster mode : read msg from FE, but NOT send to other users,
* instead, send to Redis channel, so the other service
* on cluster can read/digest the msg
*/
@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){
LOGGER.error("send msg error : " + e.getMessage(), e);
}
}

@MessageMapping("/chat.addUser")
public void addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {

LOGGER.info("User added in Chatroom:" + chatMessage.getSender());
try {
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);
}
final String onlineUserKey = "websocket.onlineUsers";
private final String USER_NAME = "username";
@Value("${redis.channel.msgToAll}")
private String msgToAll;
@Value("${redis.set.onlineUsers}")
private String onlineUsers;
@Value("${redis.channel.userStatus}")
private String userStatus;
@Value("${redis.channel.private}")
private String privateChannel;
// private RedisTemplate redisTemplate;
// TODO : check difference ? RedisTemplate VS RedisTemplate<String, String>
@Autowired private RedisTemplate<String, String> redisTemplate;
@Autowired private SimpMessagingTemplate simpMessagingTemplate;

/** single mode : read msg from FE, and send to other users (@SendTo("/topic/public")) directly */
// @MessageMapping("/chat.sendMessage")
// @SendTo("/topic/public")
// public ChatMessage sendMessage(@Payload ChatMessage chatMessage){
//
// return chatMessage;
// }

/**
* cluster mode : read msg from FE, but NOT send to other users, instead, send to Redis channel,
* so the other service on cluster can read/digest the msg
*/
@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) {
log.error("send msg error : " + e.getMessage(), e);
}

// 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);
}

@MessageMapping("/chat.addUser")
public void addUser(@Payload ChatMessage chatMessage, SimpMessageHeaderAccessor headerAccessor) {

log.info("User added in Chatroom:" + chatMessage.getSender());
try {
headerAccessor.getSessionAttributes().put(USER_NAME, chatMessage.getSender());
redisTemplate.opsForSet().add(onlineUsers, chatMessage.getSender());
redisTemplate.convertAndSend(userStatus, JsonUtil.parseObjToJson(chatMessage));
// TODO : show online user
} catch (Exception e) {
log.error(e.getMessage(), e);
}

}

// 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);
}
}
Loading

0 comments on commit a61c60e

Please sign in to comment.