diff --git a/build.gradle b/build.gradle index efa4a43452e7e9a2bf2debfa357037c30d4465be..73893fa83c57fc92870ba2b25919711f7071e446 100644 --- a/build.gradle +++ b/build.gradle @@ -24,9 +24,13 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-websocket' implementation 'com.fasterxml.jackson.module:jackson-module-kotlin' implementation 'org.jetbrains.kotlin:kotlin-reflect' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5' developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.h2database:h2' + runtimeOnly 'org.postgresql:postgresql' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/configs/WebSocketsConfig.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/configs/WebSocketsConfig.kt index 72d1e33087b7280f0ba96f4015d9c16e84c7b130..7ef2d66f6660a7a1ce676ee2599266fbee4949d8 100644 --- a/src/main/kotlin/de/thk/gm/websocketsdemo/configs/WebSocketsConfig.kt +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/configs/WebSocketsConfig.kt @@ -1,5 +1,6 @@ package de.thk.gm.websocketsdemo.configs +import de.thk.gm.websocketsdemo.handlers.ChatRoomsHandler import de.thk.gm.websocketsdemo.handlers.EchoHandler import de.thk.gm.websocketsdemo.handlers.SimpleChatHandler import org.springframework.context.annotation.Configuration @@ -14,5 +15,6 @@ class WebSocketsConfig(): WebSocketConfigurer { override fun registerWebSocketHandlers(registry: WebSocketHandlerRegistry) { registry.addHandler(EchoHandler(), "/echo").setAllowedOrigins("*") registry.addHandler(SimpleChatHandler(), "/chat") + registry.addHandler(ChatRoomsHandler(),"/rooms") } } \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/controllers/ChatRoomsController.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/controllers/ChatRoomsController.kt new file mode 100644 index 0000000000000000000000000000000000000000..36fdf8c044b4095146c63d2c081dc05807b63c89 --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/controllers/ChatRoomsController.kt @@ -0,0 +1,35 @@ +package de.thk.gm.websocketsdemo.controllers + +import de.thk.gm.websocketsdemo.dtos.ChatRoomDto +import de.thk.gm.websocketsdemo.models.ChatRoom +import de.thk.gm.websocketsdemo.services.ChatRoomsService +import org.springframework.stereotype.Controller +import org.springframework.ui.Model +import org.springframework.web.bind.annotation.* +import java.util.* + +@Controller +@RequestMapping("/chatrooms") +class ChatRoomsController (private val chatRoomsService: ChatRoomsService) { + + @GetMapping + fun getChatRooms(model: Model): String { + model.addAttribute("chatRooms", chatRoomsService.getChatRooms()) + return "chatRooms/showChatRooms" + } + + @PostMapping + fun addChatRoom(chatRoomDto: ChatRoomDto): String { + var chatRoom = ChatRoom() + chatRoom.name = chatRoomDto.name + chatRoomsService.save(chatRoom) + return "redirect:/chatrooms/${chatRoom.id}" + } + + @GetMapping("/{id}") + fun getChatRoom(@PathVariable("id") id: UUID, model: Model): String { + var chatRoom = chatRoomsService.getChatRoomById(id) + model.addAttribute("chatRoom", chatRoom) + return "chatRooms/showChatRoom" + } +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/dtos/ChatRoomDto.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/dtos/ChatRoomDto.kt new file mode 100644 index 0000000000000000000000000000000000000000..2d827a1b158aaa2230e251d8a8ccb3f5c8c54620 --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/dtos/ChatRoomDto.kt @@ -0,0 +1,10 @@ +package de.thk.gm.websocketsdemo.dtos + +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size + +class ChatRoomDto { + @NotNull + @Size(min = 1, max = 50) + var name: String = "" +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/handlers/ChatRoomsHandler.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/handlers/ChatRoomsHandler.kt new file mode 100644 index 0000000000000000000000000000000000000000..9d80ede0d367e539e7d17a78dd80fe5ca9b97ddf --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/handlers/ChatRoomsHandler.kt @@ -0,0 +1,45 @@ +package de.thk.gm.websocketsdemo.handlers + +import org.springframework.web.socket.CloseStatus +import org.springframework.web.socket.TextMessage +import org.springframework.web.socket.WebSocketSession +import org.springframework.web.socket.handler.TextWebSocketHandler +import org.springframework.web.util.UriComponents +import org.springframework.web.util.UriComponentsBuilder + +class ChatRoomsHandler : TextWebSocketHandler() { + private val hashMapOfSessions : HashMap<String, ArrayList<WebSocketSession>> = HashMap() + override fun afterConnectionEstablished(session: WebSocketSession) { + var uri : UriComponents = UriComponentsBuilder.fromUri(session.uri!!).build() + var id = uri.queryParams.getFirst("id") + if(id != null) { + var sessions = hashMapOfSessions[id] + if(sessions == null) { + sessions = ArrayList() + } + sessions.add(session) + hashMapOfSessions[id] = sessions + } + } + + override fun handleTextMessage(session: WebSocketSession, message: TextMessage) { + var uri : UriComponents = UriComponentsBuilder.fromUri(session.uri!!).build() + var id = uri.queryParams.getFirst("id") + var sessions = hashMapOfSessions[id] + if(sessions != null) { + for (chatSession in sessions) { + chatSession.sendMessage(message) + } + } + } + + override fun afterConnectionClosed(session: WebSocketSession, status: CloseStatus) { + var uri : UriComponents = UriComponentsBuilder.fromUri(session.uri!!).build() + var id = uri.queryParams.getFirst("id") + var sessions = hashMapOfSessions[id] + if(sessions != null) { + sessions.remove(session) + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/models/ChatRoom.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/models/ChatRoom.kt new file mode 100644 index 0000000000000000000000000000000000000000..8d98ce62940e968758e03077b922bf210524b444 --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/models/ChatRoom.kt @@ -0,0 +1,13 @@ +package de.thk.gm.websocketsdemo.models + +import jakarta.persistence.Entity +import jakarta.persistence.Id +import java.util.* + +@Entity +class ChatRoom { + @Id + var id : UUID = UUID.randomUUID() + + var name: String = "" +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/repositories/ChatRoomsRepository.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/repositories/ChatRoomsRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..96cc7f83b47d1f12ba96bffe20bacfe210bf7c6f --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/repositories/ChatRoomsRepository.kt @@ -0,0 +1,10 @@ +package de.thk.gm.websocketsdemo.repositories + +import de.thk.gm.websocketsdemo.models.ChatRoom +import org.springframework.data.repository.CrudRepository +import org.springframework.stereotype.Repository +import java.util.* + +@Repository +interface ChatRoomsRepository : CrudRepository<ChatRoom, UUID> { +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsService.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsService.kt new file mode 100644 index 0000000000000000000000000000000000000000..fb6270b4b62ec8be8ada060589e24485bfd0f0b4 --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsService.kt @@ -0,0 +1,10 @@ +package de.thk.gm.websocketsdemo.services + +import de.thk.gm.websocketsdemo.models.ChatRoom +import java.util.* + +interface ChatRoomsService { + fun getChatRooms(): List<ChatRoom> + fun getChatRoomById(id: UUID): ChatRoom? + fun save(chatRoom: ChatRoom) +} \ No newline at end of file diff --git a/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsServiceImpl.kt b/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsServiceImpl.kt new file mode 100644 index 0000000000000000000000000000000000000000..2ed307c6b07f5eb9600314377241920c1909e752 --- /dev/null +++ b/src/main/kotlin/de/thk/gm/websocketsdemo/services/ChatRoomsServiceImpl.kt @@ -0,0 +1,21 @@ +package de.thk.gm.websocketsdemo.services + +import de.thk.gm.websocketsdemo.models.ChatRoom +import de.thk.gm.websocketsdemo.repositories.ChatRoomsRepository +import org.springframework.stereotype.Service +import java.util.* + +@Service +class ChatRoomsServiceImpl (private val chatRoomsRepository: ChatRoomsRepository) : ChatRoomsService { + override fun getChatRooms(): List<ChatRoom> { + return chatRoomsRepository.findAll().toList() + } + + override fun getChatRoomById(id: UUID): ChatRoom? { + return chatRoomsRepository.findById(id).orElse(null) + } + + override fun save(chatRoom: ChatRoom) { + chatRoomsRepository.save(chatRoom) + } +} \ No newline at end of file diff --git a/src/main/resources/templates/chatRooms/showChatRoom.ftlh b/src/main/resources/templates/chatRooms/showChatRoom.ftlh new file mode 100644 index 0000000000000000000000000000000000000000..8aaac5ef50306699d218891571c335040fccdbcd --- /dev/null +++ b/src/main/resources/templates/chatRooms/showChatRoom.ftlh @@ -0,0 +1,33 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>Chat Room</title> +</head> +<body> + <h1>Chat room ${chatRoom.name}</h1> + <input type="text" id="username" placeholder="username"> + <input type="text" id="text" placeholder="message"> + <br> + <button onclick="sendMessage()">Send</button> + <div id="chat"></div> + <script> + var ws = new WebSocket("/rooms?id=${chatRoom.id}") + var chat = document.getElementById("chat") + var message = {} + function sendMessage(){ + message.username = document.getElementById("username").value + message.text = document.getElementById("text").value + ws.send(JSON.stringify(message)) + } + + ws.onmessage = function (msg) { + message = JSON.parse(msg.data) + chat.innerHTML += "<p><b>"+message.username+":</b>" + message.text + "</p>" + } + </script> +</body> +</html> \ No newline at end of file diff --git a/src/main/resources/templates/chatRooms/showChatRooms.ftlh b/src/main/resources/templates/chatRooms/showChatRooms.ftlh new file mode 100644 index 0000000000000000000000000000000000000000..69116bcc0f31e49c1871f3e8ed906c3915aa91fc --- /dev/null +++ b/src/main/resources/templates/chatRooms/showChatRooms.ftlh @@ -0,0 +1,22 @@ +<!doctype html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" + content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <title>Chat Rooms</title> +</head> +<body> + <h1>Chat Rooms</h1> + <ul> + <#list chatRooms as room> + <li><a href="/chatrooms/${room.id}">${room.name}</a></li> + </#list> + </ul><br> + <form action="/chatrooms" method="post"> + <input name="name" type="text"><br> + <button>Create chat room</button> + </form> +</body> +</html> \ No newline at end of file