diff --git a/src/main/java/de/w665/testing/config/WebSocketConfig.java b/src/main/java/de/w665/testing/config/WebSocketConfig.java index 6110b2b..10f6482 100644 --- a/src/main/java/de/w665/testing/config/WebSocketConfig.java +++ b/src/main/java/de/w665/testing/config/WebSocketConfig.java @@ -18,6 +18,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/app"); - registry.enableSimpleBroker("/topic"); + registry.enableSimpleBroker("/topic", "/queue"); + registry.setUserDestinationPrefix("/user"); } } diff --git a/src/main/java/de/w665/testing/controller/ChatController.java b/src/main/java/de/w665/testing/controller/ChatController.java index 6ab3a4b..1ed1be1 100644 --- a/src/main/java/de/w665/testing/controller/ChatController.java +++ b/src/main/java/de/w665/testing/controller/ChatController.java @@ -1,27 +1,54 @@ package de.w665.testing.controller; import de.w665.testing.model.ChatMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.context.event.EventListener; +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.SimpMessageHeaderAccessor; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Controller; +import org.springframework.web.socket.messaging.SessionDisconnectEvent; @Controller +@RequiredArgsConstructor public class ChatController { - @MessageMapping("/chat.sendMessage") - @SendTo("/topic/public") - public ChatMessage sendMessage(@Payload ChatMessage chatMessage) { - return chatMessage; + private final SimpMessagingTemplate messagingTemplate; + + @MessageMapping("/chat/{roomId}/sendMessage") + public void sendMessage(@DestinationVariable String roomId, @Payload ChatMessage chatMessage) { + messagingTemplate.convertAndSend(String.format("/topic/rooms/%s", roomId), chatMessage); } - @MessageMapping("/chat.addUser") - @SendTo("/topic/public") - public ChatMessage addUser(@Payload ChatMessage chatMessage, - SimpMessageHeaderAccessor headerAccessor) { - // Add username in web socket session + @MessageMapping("/chat/{roomId}/addUser") + public void addUser(@DestinationVariable String roomId, @Payload ChatMessage chatMessage, + SimpMessageHeaderAccessor headerAccessor) { + String currentRoomId = (String) headerAccessor.getSessionAttributes().put("room_id", roomId); + if (currentRoomId != null) { + ChatMessage leaveMessage = new ChatMessage(); + leaveMessage.setType(ChatMessage.MessageType.LEAVE); + leaveMessage.setSender(chatMessage.getSender()); + messagingTemplate.convertAndSend(String.format("/topic/rooms/%s", currentRoomId), leaveMessage); + } headerAccessor.getSessionAttributes().put("username", chatMessage.getSender()); - return chatMessage; + messagingTemplate.convertAndSend(String.format("/topic/rooms/%s", roomId), chatMessage); + } + + @EventListener + public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { + SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(event.getMessage()); + + String username = (String) headerAccessor.getSessionAttributes().get("username"); + String roomId = (String) headerAccessor.getSessionAttributes().get("room_id"); + + if (username != null && roomId != null) { + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessage.MessageType.LEAVE); + chatMessage.setSender(username); + + messagingTemplate.convertAndSend(String.format("/topic/rooms/%s", roomId), chatMessage); + } } } diff --git a/src/main/java/de/w665/testing/controller/ChatRoomController.java b/src/main/java/de/w665/testing/controller/ChatRoomController.java new file mode 100644 index 0000000..19722f9 --- /dev/null +++ b/src/main/java/de/w665/testing/controller/ChatRoomController.java @@ -0,0 +1,34 @@ +package de.w665.testing.controller; + +import de.w665.testing.model.ChatRoom; +import de.w665.testing.service.ChatRoomService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Collection; + +@RestController +@RequestMapping("/api/chat-rooms") +@RequiredArgsConstructor +public class ChatRoomController { + + private final ChatRoomService chatRoomService; + + @PostMapping + public ChatRoom createRoom(@RequestBody CreateRoomRequest request) { + return chatRoomService.createChatRoom(request.name(), request.password()); + } + + @GetMapping + public Collection getRooms() { + return chatRoomService.findAll(); + } + + @PostMapping("/{roomId}/join") + public boolean joinRoom(@PathVariable String roomId, @RequestBody JoinRoomRequest request) { + return chatRoomService.joinRoom(roomId, request.password()); + } + + public record CreateRoomRequest(String name, String password) {} + public record JoinRoomRequest(String password) {} +} diff --git a/src/main/java/de/w665/testing/model/ChatMessage.java b/src/main/java/de/w665/testing/model/ChatMessage.java index db64a34..e8dcebe 100644 --- a/src/main/java/de/w665/testing/model/ChatMessage.java +++ b/src/main/java/de/w665/testing/model/ChatMessage.java @@ -6,6 +6,12 @@ import lombok.Setter; @Getter @Setter public class ChatMessage { + + public enum MessageType { + CHAT, JOIN, LEAVE + } + + private MessageType type; private String content; private String sender; } diff --git a/src/main/java/de/w665/testing/model/ChatRoom.java b/src/main/java/de/w665/testing/model/ChatRoom.java new file mode 100644 index 0000000..276225e --- /dev/null +++ b/src/main/java/de/w665/testing/model/ChatRoom.java @@ -0,0 +1,32 @@ +package de.w665.testing.model; + +import lombok.Getter; +import lombok.Setter; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +@Getter +@Setter +public class ChatRoom { + + private String id; + private String name; + private String password; + private Set users = new HashSet<>(); + + public ChatRoom(String name, String password) { + this.id = UUID.randomUUID().toString(); + this.name = name; + this.password = password; + } + + public void addUser(String username) { + users.add(username); + } + + public void removeUser(String username) { + users.remove(username); + } +} diff --git a/src/main/java/de/w665/testing/service/ChatRoomService.java b/src/main/java/de/w665/testing/service/ChatRoomService.java new file mode 100644 index 0000000..45a8187 --- /dev/null +++ b/src/main/java/de/w665/testing/service/ChatRoomService.java @@ -0,0 +1,35 @@ +package de.w665.testing.service; + +import de.w665.testing.model.ChatRoom; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class ChatRoomService { + + private final Map chatRooms = new ConcurrentHashMap<>(); + + public ChatRoom createChatRoom(String name, String password) { + ChatRoom chatRoom = new ChatRoom(name, password); + chatRooms.put(chatRoom.getId(), chatRoom); + return chatRoom; + } + + public Optional findById(String id) { + return Optional.ofNullable(chatRooms.get(id)); + } + + public Collection findAll() { + return chatRooms.values(); + } + + public boolean joinRoom(String roomId, String password) { + return findById(roomId) + .map(room -> room.getPassword() == null || room.getPassword().isEmpty() || room.getPassword().equals(password)) + .orElse(false); + } +} diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 56c48b8..c518080 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -4,50 +4,28 @@ QChat -
+
+
-
+
-
-

Enter Your Name

-
+

Enter Your Name

- +
@@ -56,13 +34,29 @@
+ +
+
+

Chat Rooms

+
+

Create a New Room

+
+ + + +
+

Available Rooms

+
    +
    +
    +
    + +
    -
    -

    QChat

    -
    +

      -
      +
      @@ -78,70 +72,123 @@