blog : chatting - init

This commit is contained in:
haerong22
2022-08-20 16:06:12 +09:00
parent 7c53b4581a
commit 103a4ee945
10 changed files with 198 additions and 14 deletions

View File

@@ -1,6 +1,10 @@
package com.example.chatting.config;
import com.example.chatting.redis.RedisSubscriber;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -11,7 +15,8 @@ import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactor
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Slf4j
@@ -27,6 +32,14 @@ public class RedisConfig {
@Value("${spring.redis.password}")
private String password;
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModules(new JavaTimeModule(), new Jdk8Module());
return mapper;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
@@ -43,7 +56,7 @@ public class RedisConfig {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper()));
return redisTemplate;
}
@@ -52,7 +65,12 @@ public class RedisConfig {
RedisSubscriber redisSubscriber) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(redisSubscriber, new ChannelTopic("message"));
container.addMessageListener(messageListenerAdapter(redisSubscriber), new ChannelTopic("message"));
return container;
}
@Bean
public MessageListenerAdapter messageListenerAdapter(RedisSubscriber redisSubscriber) {
return new MessageListenerAdapter(redisSubscriber, "sendMessage");
}
}

View File

@@ -1,19 +1,28 @@
package com.example.chatting.config;
import com.example.chatting.handler.HandshakeHandler;
import com.example.chatting.handler.MessageHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@RequiredArgsConstructor
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private final MessageHandler messageHandler;
private final HandshakeHandler handshakeHandler;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-stomp")
.setAllowedOriginPatterns("*")
.setHandshakeHandler(handshakeHandler)
.withSockJS();
}
@@ -22,4 +31,9 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
registry.setApplicationDestinationPrefixes("/pub");
registry.enableSimpleBroker("/sub");
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(messageHandler);
}
}

View File

@@ -1,18 +1,62 @@
package com.example.chatting.controller;
import com.example.chatting.domain.dto.ChatMessage;
import com.example.chatting.redis.RedisPublisher;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.Principal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class MessageController {
private final RedisPublisher redisPublisher;
private final RedisTemplate<String, Object> redisTemplate;
private final ObjectMapper objectMapper;
@MessageMapping("/message")
public void message(String message) {
redisPublisher.publish("message", message);
public void message(Principal principal, ChatMessage chatMessage) throws JsonProcessingException {
System.out.println("controller");
chatMessage.setSessionId(principal.getName());
redisPublisher.publish("message", chatMessage);
}
@ResponseBody
@GetMapping("/test")
public void test() {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setSessionId("session");
chatMessage.setMessage("hello");
ChatMessage chatMessage2 = new ChatMessage();
chatMessage2.setSessionId("session");
chatMessage2.setMessage("hello");
chatMessage2.setDate(LocalDateTime.now());
chatMessage2.setTestEnum(ChatMessage.TestEnum.A);
List<ChatMessage> testList = new ArrayList<>();
testList.add(chatMessage);
testList.add(chatMessage2);
ValueOperations<String, Object> ops = redisTemplate.opsForValue();
ops.set("test", testList);
List<ChatMessage> test = (List<ChatMessage>) ops.get("test");
System.out.println("test = " + test);
// redisPublisher.publish("message", chatMessage);
}
}

View File

@@ -0,0 +1,18 @@
package com.example.chatting.domain.dto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ChatMessage {
public enum TestEnum {
A, B, C
}
private String sessionId;
private String message;
private LocalDateTime date;
private TestEnum testEnum;
}

View File

@@ -0,0 +1,19 @@
package com.example.chatting.handler;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import java.security.Principal;
import java.util.Map;
import java.util.UUID;
@Component
public class HandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
return new StompPrincipal(UUID.randomUUID().toString());
}
}

View File

@@ -0,0 +1,46 @@
package com.example.chatting.handler;
import com.example.chatting.domain.dto.ChatMessage;
import com.example.chatting.redis.RedisPublisher;
import lombok.RequiredArgsConstructor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class MessageHandler implements ChannelInterceptor {
private final RedisPublisher redisPublisher;
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
StompPrincipal simpUser = (StompPrincipal) accessor.getHeader("simpUser");
System.out.println("simpUser = " + simpUser);
String destination = accessor.getDestination();
StompCommand command = accessor.getCommand();
String sessionId = accessor.getSessionId();
SimpMessageType messageType = accessor.getMessageType();
if (accessor.getCommand() == StompCommand.SUBSCRIBE) {
System.out.println("destination = " + destination);
System.out.println("command = " + command);
System.out.println("sessionId = " + sessionId);
System.out.println("messageType = " + messageType);
ChatMessage chatMessage = new ChatMessage();
chatMessage.setSessionId("session");
chatMessage.setMessage("interceptor");
redisPublisher.publish("message", chatMessage);
}
return message;
}
}

View File

@@ -0,0 +1,20 @@
package com.example.chatting.handler;
import lombok.ToString;
import java.security.Principal;
@ToString
public class StompPrincipal implements Principal {
private final String name;
public StompPrincipal(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}

View File

@@ -1,5 +1,6 @@
package com.example.chatting.redis;
import com.example.chatting.domain.dto.ChatMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@@ -10,7 +11,7 @@ public class RedisPublisher {
private final RedisTemplate<String, Object> redisTemplate;
public void publish(String topic, String message) {
public void publish(String topic, ChatMessage message) {
redisTemplate.convertAndSend(topic, message);
}
}

View File

@@ -1,10 +1,9 @@
package com.example.chatting.redis;
import com.example.chatting.domain.dto.ChatMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Service;
@@ -12,17 +11,17 @@ import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class RedisSubscriber implements MessageListener {
public class RedisSubscriber {
private final ObjectMapper objectMapper;
private final RedisTemplate<String, Object> redisTemplate;
private final SimpMessageSendingOperations messagingTemplate;
@Override
public void onMessage(Message message, byte[] pattern) {
public void sendMessage(String message) {
try {
String pubMessage = redisTemplate.getStringSerializer().deserialize(message.getBody());
messagingTemplate.convertAndSend("/sub/message", pubMessage == null ? "" : pubMessage);
ChatMessage chatMessage = objectMapper.readValue(message, ChatMessage.class);
messagingTemplate.convertAndSend("/sub/message", chatMessage);
// messagingTemplate.convertAndSendToUser(chatMessage.getSessionId(), "/sub/message", chatMessage);
} catch (Exception e) {
log.error("Subscriber Error", e);
}

View File

@@ -1,5 +1,10 @@
server:
port: 28080
spring:
redis:
host: localhost
port: 6379
password: 1234
password: 1234