diff --git a/pom.xml b/pom.xml index 1b26c5b2..54cc4037 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,23 @@ + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-data-rest + + + org.springframework.boot + spring-boot-starter-web + + + com.h2database + h2 + runtime + org.springframework.boot spring-boot-starter-websocket @@ -41,7 +58,28 @@ org.springframework.boot spring-boot-starter-reactor-netty - + + org.webjars + sockjs-client + 1.1.2 + + + org.webjars + stomp-websocket + 2.3.3 + + + + org.webjars + jquery + 3.1.1 + + + + org.webjars + bootstrap + 3.3.5 + org.springframework.boot spring-boot-devtools diff --git a/src/main/java/com/example/websocketdemo/api/RestController.java b/src/main/java/com/example/websocketdemo/api/RestController.java new file mode 100644 index 00000000..30577ee2 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/api/RestController.java @@ -0,0 +1,34 @@ +package com.example.websocketdemo.api; + +import com.example.websocketdemo.models.Message; +import com.example.websocketdemo.models.User; +import com.example.websocketdemo.services.MessageService; +import com.example.websocketdemo.services.UserService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; + +@org.springframework.web.bind.annotation.RestController +@RequestMapping("/api") +public class RestController { + + private final UserService userService; + private final MessageService messageService; + + public RestController(UserService userService, MessageService messageService) { + this.userService = userService; + this.messageService=messageService; + } + + @GetMapping("/users") + List getAllUsers(){ + return userService.findAll(); + } + + @GetMapping("/messages") + List getMessages(){ + return messageService.findAll(); + } + +} diff --git a/src/main/java/com/example/websocketdemo/controller/ChatController.java b/src/main/java/com/example/websocketdemo/controller/ChatController.java index ac4e6055..098e3b83 100644 --- a/src/main/java/com/example/websocketdemo/controller/ChatController.java +++ b/src/main/java/com/example/websocketdemo/controller/ChatController.java @@ -1,6 +1,12 @@ package com.example.websocketdemo.controller; -import com.example.websocketdemo.model.ChatMessage; +import com.example.websocketdemo.models.Message; +import com.example.websocketdemo.models.MessageDTO; +import com.example.websocketdemo.models.User; +import com.example.websocketdemo.services.MessageService; +import com.example.websocketdemo.services.UserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.handler.annotation.SendTo; @@ -12,20 +18,40 @@ */ @Controller public class ChatController { + private final UserService userService; + private final MessageService messageService; + private static Logger logger= LoggerFactory.getLogger(ChatController.class); + + public ChatController(UserService userService, MessageService messageService) { + this.userService = userService; + this.messageService = messageService; + } @MessageMapping("/chat.sendMessage") @SendTo("/topic/public") - public ChatMessage sendMessage(@Payload ChatMessage chatMessage) { - return chatMessage; + public Message sendMessage(@Payload MessageDTO messageDTO) { + Message message=new Message(); + User user=userService.findByUsername(messageDTO.getUsername()); + message.setAuthor(user); + message.setContent(messageDTO.getContent()); + message.setMessageType(messageDTO.getType()); + System.out.println(messageDTO); + messageService.save(message); + logger.info(">>>"+message.getAuthor().getUsername()+ " sent a message"); + return message; } @MessageMapping("/chat.addUser") @SendTo("/topic/public") - public ChatMessage addUser(@Payload ChatMessage chatMessage, + public MessageDTO addUser(@Payload MessageDTO message, SimpMessageHeaderAccessor headerAccessor) { // Add username in web socket session - headerAccessor.getSessionAttributes().put("username", chatMessage.getSender()); - return chatMessage; + headerAccessor.getSessionAttributes().put("username", message.getUsername()); + User user=new User(); + user.setUsername(message.getUsername()); + userService.save(user); + logger.info(message.getUsername()+" joined the chat ! Great !"); + return message; } } diff --git a/src/main/java/com/example/websocketdemo/controller/WebSocketEventListener.java b/src/main/java/com/example/websocketdemo/controller/WebSocketEventListener.java index f732c400..dbe8b7e5 100644 --- a/src/main/java/com/example/websocketdemo/controller/WebSocketEventListener.java +++ b/src/main/java/com/example/websocketdemo/controller/WebSocketEventListener.java @@ -1,6 +1,10 @@ package com.example.websocketdemo.controller; -import com.example.websocketdemo.model.ChatMessage; + +import com.example.websocketdemo.models.MessageDTO; +import com.example.websocketdemo.models.MessageType; +import com.example.websocketdemo.models.User; +import com.example.websocketdemo.services.UserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -34,10 +38,9 @@ public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) { String username = (String) headerAccessor.getSessionAttributes().get("username"); if(username != null) { logger.info("User Disconnected : " + username); - - ChatMessage chatMessage = new ChatMessage(); - chatMessage.setType(ChatMessage.MessageType.LEAVE); - chatMessage.setSender(username); + MessageDTO chatMessage = new MessageDTO(); + chatMessage.setType(MessageType.LEAVE); + chatMessage.setUsername(username); messagingTemplate.convertAndSend("/topic/public", chatMessage); } diff --git a/src/main/java/com/example/websocketdemo/model/ChatMessage.java b/src/main/java/com/example/websocketdemo/model/ChatMessage.java deleted file mode 100644 index b158d1ad..00000000 --- a/src/main/java/com/example/websocketdemo/model/ChatMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.websocketdemo.model; - -/** - * Created by rajeevkumarsingh on 24/07/17. - */ -public class ChatMessage { - private MessageType type; - private String content; - private String sender; - - public enum MessageType { - CHAT, - JOIN, - LEAVE - } - - 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; - } -} diff --git a/src/main/java/com/example/websocketdemo/models/Message.java b/src/main/java/com/example/websocketdemo/models/Message.java new file mode 100644 index 00000000..ce412361 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/models/Message.java @@ -0,0 +1,74 @@ +package com.example.websocketdemo.models; + +import com.fasterxml.jackson.annotation.JsonManagedReference; + +import javax.persistence.Entity; +import javax.persistence.*; + +import java.util.Date; +import java.util.UUID; + +import static javax.persistence.FetchType.EAGER; + +@Entity +public class Message { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + private String content; + private Date created=new Date(); + + @JsonManagedReference + @ManyToOne(fetch=EAGER) + @JoinColumn(name="fk_author") + private User author; + + @Enumerated(value=EnumType.STRING) + private MessageType messageType; + + public MessageType getMessageType() { + return messageType; + } + + public void setMessageType(MessageType messageType) { + this.messageType = messageType; + } + + public UUID getId() { + return id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Date getCreated() { + return created; + } + + + public User getAuthor() { + return author; + } + + public void setAuthor(User author) { + this.author = author; + } + + @Override + public String toString() { + return "Message{" + + "id=" + id + + ", content='" + content + '\'' + + ", created=" + created + + ", author=" + author + + ", messageType=" + messageType + + '}'; + } +} diff --git a/src/main/java/com/example/websocketdemo/models/MessageDTO.java b/src/main/java/com/example/websocketdemo/models/MessageDTO.java new file mode 100644 index 00000000..5b23e827 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/models/MessageDTO.java @@ -0,0 +1,43 @@ +package com.example.websocketdemo.models; + +public class MessageDTO { + + + private String username; + private String content; + private MessageType type; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + + public MessageType getType() { + return type; + } + + public void setType(MessageType type) { + this.type = type; + } + + @Override + public String toString() { + return "MessageDTO{" + + "username='" + username + '\'' + + ", content='" + content + '\'' + + ", type=" + type + + '}'; + } +} diff --git a/src/main/java/com/example/websocketdemo/models/MessageType.java b/src/main/java/com/example/websocketdemo/models/MessageType.java new file mode 100644 index 00000000..a955a111 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/models/MessageType.java @@ -0,0 +1,5 @@ +package com.example.websocketdemo.models; + +public enum MessageType { + JOIN, CHAT, LEAVE +} diff --git a/src/main/java/com/example/websocketdemo/models/User.java b/src/main/java/com/example/websocketdemo/models/User.java new file mode 100644 index 00000000..8b3d0ef0 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/models/User.java @@ -0,0 +1,62 @@ +package com.example.websocketdemo.models; + +import com.fasterxml.jackson.annotation.JsonBackReference; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Entity +public class User { + + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private UUID id; + + private String username; + + @JsonBackReference + @OneToMany(cascade=CascadeType.ALL, mappedBy="author") + private List messages=new ArrayList<>(); + + public User(){}; + + public User(String username) { + this.username = username; + } + + public UUID getId() { + return id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public List getMessages() { + return messages; + } + + public void setMessages(List messages) { + this.messages = messages; + } + + public void newMessage(Message message){ + this.messages.add(message); + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", username='" + username + '\'' + + ", messages=" + messages + + '}'; + } +} diff --git a/src/main/java/com/example/websocketdemo/repository/MessageRepository.java b/src/main/java/com/example/websocketdemo/repository/MessageRepository.java new file mode 100644 index 00000000..10f6dff8 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/repository/MessageRepository.java @@ -0,0 +1,9 @@ +package com.example.websocketdemo.repository; + +import com.example.websocketdemo.models.Message; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface MessageRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/websocketdemo/repository/UserRepository.java b/src/main/java/com/example/websocketdemo/repository/UserRepository.java new file mode 100644 index 00000000..0800f46c --- /dev/null +++ b/src/main/java/com/example/websocketdemo/repository/UserRepository.java @@ -0,0 +1,13 @@ +package com.example.websocketdemo.repository; + +import com.example.websocketdemo.models.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); +} diff --git a/src/main/java/com/example/websocketdemo/services/MessageService.java b/src/main/java/com/example/websocketdemo/services/MessageService.java new file mode 100644 index 00000000..e8f7613d --- /dev/null +++ b/src/main/java/com/example/websocketdemo/services/MessageService.java @@ -0,0 +1,10 @@ +package com.example.websocketdemo.services; + +import com.example.websocketdemo.models.Message; + +import java.util.List; + +public interface MessageService { + void save(Message message); + List findAll(); +} diff --git a/src/main/java/com/example/websocketdemo/services/MessageServiceImpl.java b/src/main/java/com/example/websocketdemo/services/MessageServiceImpl.java new file mode 100644 index 00000000..a929195d --- /dev/null +++ b/src/main/java/com/example/websocketdemo/services/MessageServiceImpl.java @@ -0,0 +1,26 @@ +package com.example.websocketdemo.services; + +import com.example.websocketdemo.models.Message; +import com.example.websocketdemo.repository.MessageRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class MessageServiceImpl implements MessageService{ + private final MessageRepository messageRepository; + + public MessageServiceImpl(MessageRepository messageRepository) { + this.messageRepository = messageRepository; + } + + @Override + public void save(Message message) { + messageRepository.save(message); + } + + @Override + public List findAll() { + return messageRepository.findAll(); + } +} diff --git a/src/main/java/com/example/websocketdemo/services/UserService.java b/src/main/java/com/example/websocketdemo/services/UserService.java new file mode 100644 index 00000000..02dca61f --- /dev/null +++ b/src/main/java/com/example/websocketdemo/services/UserService.java @@ -0,0 +1,11 @@ +package com.example.websocketdemo.services; + +import com.example.websocketdemo.models.User; + +import java.util.List; + +public interface UserService { + void save(User user); + User findByUsername(String username); + List findAll(); +} diff --git a/src/main/java/com/example/websocketdemo/services/UserServiceImpl.java b/src/main/java/com/example/websocketdemo/services/UserServiceImpl.java new file mode 100644 index 00000000..f76f9aa2 --- /dev/null +++ b/src/main/java/com/example/websocketdemo/services/UserServiceImpl.java @@ -0,0 +1,38 @@ +package com.example.websocketdemo.services; + +import com.example.websocketdemo.models.User; +import com.example.websocketdemo.repository.UserRepository; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class UserServiceImpl implements UserService{ + + private final UserRepository userRepository; + + public UserServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public void save(User user) { + userRepository.save(user); + } + + @Override + public User findByUsername(String username) { + Optional userOptional=userRepository.findByUsername(username); + if(! userOptional.isPresent()){ + throw new RuntimeException("User not found"); + } + User user=userOptional.get(); + return user; + } + + @Override + public List findAll() { + return userRepository.findAll(); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29b..82d64d61 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,10 @@ +#JPA +spring.jpa.generate-ddl=true +spring.jpa.hibernate.dll-auto=create-drop + +#H2 +spring.h2.console.enabled=true +spring.datasource.url=jdbc:h2:mem:testdb + +#Rest Repositories +spring.data.rest.base-path=/api \ No newline at end of file diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index 0582ba3e..40749359 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -39,7 +39,7 @@ function onConnected() { // Tell your username to the server stompClient.send("/app/chat.addUser", {}, - JSON.stringify({sender: username, type: 'JOIN'}) + JSON.stringify({username: username, type: 'JOIN'}) ) connectingElement.classList.add('hidden'); @@ -57,7 +57,7 @@ function sendMessage(event) { if(messageContent && stompClient) { var chatMessage = { - sender: username, + username: username, content: messageInput.value, type: 'CHAT' }; @@ -76,22 +76,22 @@ function onMessageReceived(payload) { if(message.type === 'JOIN') { messageElement.classList.add('event-message'); - message.content = message.sender + ' joined!'; + message.content = message.username + ' joined!'; } else if (message.type === 'LEAVE') { messageElement.classList.add('event-message'); - message.content = message.sender + ' left!'; + message.content = message.username + ' left!'; } else { messageElement.classList.add('chat-message'); var avatarElement = document.createElement('i'); - var avatarText = document.createTextNode(message.sender[0]); + var avatarText = document.createTextNode(message.author.username[0]); avatarElement.appendChild(avatarText); - avatarElement.style['background-color'] = getAvatarColor(message.sender); + avatarElement.style['background-color'] = getAvatarColor(message.author.username); messageElement.appendChild(avatarElement); var usernameElement = document.createElement('span'); - var usernameText = document.createTextNode(message.sender); + var usernameText = document.createTextNode(message.author.username); usernameElement.appendChild(usernameText); messageElement.appendChild(usernameElement); }