mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-22 15:25:49 +00:00
Starting WebSocket
This commit is contained in:
parent
ad9d5f513c
commit
811f0852df
20 changed files with 201 additions and 60 deletions
|
@ -17,16 +17,14 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
|
|||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/websocket")
|
||||
.withSockJS();
|
||||
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
|
||||
stompEndpointRegistry.addEndpoint("/websocket").withSockJS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
config.setApplicationDestinationPrefixes("/app");
|
||||
config.enableSimpleBroker("/topic/");
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
registry.enableSimpleBroker("/topic");
|
||||
registry.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@ public class WebSocketSchedulerConfiguration {
|
|||
@Autowired
|
||||
SimpMessagingTemplate simpMessagingTemplate;
|
||||
|
||||
@Scheduled(fixedDelay = 3000)
|
||||
//@Scheduled(fixedDelay = 3000)
|
||||
public void sendMessages() {
|
||||
|
||||
simpMessagingTemplate.convertAndSend("/topic/user", new WebSocketMessage("Fixed Delay Scheduler"));
|
||||
simpMessagingTemplate.convertAndSend("/topic/public", new WebSocketMessage("Fixed Delay Scheduler"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.springframework.samples.petclinic.common.CommonAttribute;
|
|||
import org.springframework.samples.petclinic.common.CommonEndPoint;
|
||||
import org.springframework.samples.petclinic.common.CommonError;
|
||||
import org.springframework.samples.petclinic.common.CommonView;
|
||||
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
|
||||
import org.springframework.samples.petclinic.dto.*;
|
||||
import org.springframework.samples.petclinic.service.OwnerService;
|
||||
import org.springframework.samples.petclinic.service.VisitService;
|
||||
|
@ -41,7 +42,7 @@ import java.util.Map;
|
|||
* @author Paul-Emmanuel DOS SANTOS FACAO
|
||||
*/
|
||||
@Controller
|
||||
class OwnerController {
|
||||
class OwnerController extends WebSocketSender {
|
||||
|
||||
private final OwnerService ownerService;
|
||||
|
||||
|
@ -68,10 +69,12 @@ class OwnerController {
|
|||
public String processCreationForm(@ModelAttribute(CommonAttribute.OWNER) @Valid OwnerDTO owner,
|
||||
BindingResult result) {
|
||||
if (result.hasErrors()) {
|
||||
sendMessages(CommonView.OWNER_CREATE_OR_UPDATE);
|
||||
return CommonView.OWNER_CREATE_OR_UPDATE;
|
||||
}
|
||||
else {
|
||||
owner = this.ownerService.save(owner);
|
||||
sendMessages(OWNER_CREATED);
|
||||
return CommonView.OWNER_OWNERS_R + owner.getId();
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +82,7 @@ class OwnerController {
|
|||
@GetMapping(CommonEndPoint.OWNERS_FIND)
|
||||
public String initFindForm(Map<String, Object> model) {
|
||||
model.put(CommonAttribute.OWNER, new OwnerDTO());
|
||||
|
||||
return CommonView.OWNER_FIND_OWNERS;
|
||||
}
|
||||
|
||||
|
@ -107,7 +111,8 @@ class OwnerController {
|
|||
else {
|
||||
// multiple owners found
|
||||
model.put(CommonAttribute.SELECTIONS, results);
|
||||
return CommonView.OWNER_OWNERS_LIST;
|
||||
|
||||
return CommonView.OWNER_OWNERS_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +132,7 @@ class OwnerController {
|
|||
else {
|
||||
owner.setId(ownerId);
|
||||
this.ownerService.save(owner);
|
||||
sendMessages(OWNER_UPDATED);
|
||||
return CommonView.OWNER_OWNERS_ID_R;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.springframework.samples.petclinic.common.CommonAttribute;
|
|||
import org.springframework.samples.petclinic.common.CommonEndPoint;
|
||||
import org.springframework.samples.petclinic.common.CommonError;
|
||||
import org.springframework.samples.petclinic.common.CommonView;
|
||||
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
|
||||
import org.springframework.samples.petclinic.dto.*;
|
||||
import org.springframework.samples.petclinic.validator.PetDTOValidator;
|
||||
import org.springframework.samples.petclinic.service.*;
|
||||
|
@ -41,7 +42,7 @@ import java.util.Collection;
|
|||
*/
|
||||
@Controller
|
||||
@RequestMapping(CommonEndPoint.OWNERS_ID)
|
||||
class PetController {
|
||||
class PetController extends WebSocketSender {
|
||||
|
||||
private final OwnerService ownerService;
|
||||
|
||||
|
@ -100,6 +101,7 @@ class PetController {
|
|||
}
|
||||
else {
|
||||
this.petService.save(pet);
|
||||
sendMessages(PET_CREATED);
|
||||
return CommonView.OWNER_OWNERS_ID_R;
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +124,7 @@ class PetController {
|
|||
else {
|
||||
owner.addPet(pet);
|
||||
this.petService.save(pet);
|
||||
sendMessages(PET_UPDATED);
|
||||
return CommonView.OWNER_OWNERS_ID_R;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.controller;
|
|||
import org.springframework.samples.petclinic.common.CommonAttribute;
|
||||
import org.springframework.samples.petclinic.common.CommonEndPoint;
|
||||
import org.springframework.samples.petclinic.common.CommonView;
|
||||
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
|
||||
import org.springframework.samples.petclinic.dto.VetsDTO;
|
||||
import org.springframework.samples.petclinic.service.VetService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.validation.Valid;
|
|||
import org.springframework.samples.petclinic.common.CommonAttribute;
|
||||
import org.springframework.samples.petclinic.common.CommonEndPoint;
|
||||
import org.springframework.samples.petclinic.common.CommonView;
|
||||
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
|
||||
import org.springframework.samples.petclinic.dto.PetDTO;
|
||||
import org.springframework.samples.petclinic.dto.VisitDTO;
|
||||
import org.springframework.samples.petclinic.service.PetService;
|
||||
|
@ -46,7 +47,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
|||
* @author Paul-Emmanuel DOS SANTOS FACAO
|
||||
*/
|
||||
@Controller
|
||||
class VisitController {
|
||||
class VisitController extends WebSocketSender {
|
||||
|
||||
private final VisitService visitService;
|
||||
|
||||
|
@ -99,6 +100,7 @@ class VisitController {
|
|||
}
|
||||
else {
|
||||
this.visitService.save(visit);
|
||||
sendMessages(VISIT_CREATED);
|
||||
return CommonView.OWNER_OWNERS_ID_R;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package org.springframework.samples.petclinic.controller.common;
|
||||
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.SendTo;
|
||||
import org.springframework.samples.petclinic.model.common.User;
|
||||
import org.springframework.samples.petclinic.model.common.WebSocketMessage;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class UserController {
|
||||
|
||||
|
||||
@MessageMapping("/user")
|
||||
@SendTo("/topic/user")
|
||||
public WebSocketMessage getUser(User user) {
|
||||
|
||||
return new WebSocketMessage("Hi " + user.getFirstName() + user.getLastName());
|
||||
}
|
||||
}
|
|
@ -7,11 +7,10 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
|||
import org.springframework.samples.petclinic.model.common.WebSocketMessage;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class WebSocketController {
|
||||
|
||||
@MessageMapping("/websocket.send")
|
||||
@SendTo("/topic/public")
|
||||
@MessageMapping("/user")
|
||||
@SendTo("/topic/user")
|
||||
public WebSocketMessage sendMessage(@Payload final WebSocketMessage message) {
|
||||
return message;
|
||||
}
|
||||
|
@ -19,7 +18,7 @@ public class WebSocketController {
|
|||
@MessageMapping("/websocket.newUser")
|
||||
@SendTo("/topic/public")
|
||||
public WebSocketMessage newUser(@Payload final WebSocketMessage message, SimpMessageHeaderAccessor headerAccessor) {
|
||||
headerAccessor.getSessionAttributes().put("usqername", message.getSender());
|
||||
headerAccessor.getSessionAttributes().put("username", message.getSender());
|
||||
return message;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ public class WebSocketEventListener {
|
|||
|
||||
final StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(event.getMessage());
|
||||
|
||||
final String username = (String) headerAccessor.getSessionAttributes().get("username");
|
||||
// final String username = (String)
|
||||
// headerAccessor.getSessionAttributes().get("username");
|
||||
|
||||
sendingOperations.convertAndSend("/topic/public", new WebSocketMessage("Hello" + username));
|
||||
sendingOperations.convertAndSend("/topic/user", new WebSocketMessage("Hello in event lmistener"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.springframework.samples.petclinic.controller.common;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.samples.petclinic.model.common.WebSocketMessage;
|
||||
|
||||
public class WebSocketSender {
|
||||
public static final String OWNER_CREATED = "Owner created";
|
||||
public static final String OWNER_UPDATED = "Owner updated";
|
||||
public static final String OWNER_DELETED = "Owner deleted";
|
||||
|
||||
public static final String PET_CREATED = "Pet created";
|
||||
public static final String PET_UPDATED = "Pet updated";
|
||||
public static final String PET_DELETED = "Pet deleted";
|
||||
|
||||
public static final String VET_CREATED = "Vet created";
|
||||
public static final String VET_UPDATED = "Vet updated";
|
||||
public static final String VET_DELETED = "Vet deleted";
|
||||
|
||||
public static final String VISIT_CREATED = "Visit created";
|
||||
public static final String VISIT_UPDATED = "Visit updated";
|
||||
public static final String VISIT_DELETED = "Visit deleted";
|
||||
|
||||
|
||||
@Autowired
|
||||
SimpMessagingTemplate simpMessagingTemplate;
|
||||
|
||||
public void sendMessages(String message) {
|
||||
simpMessagingTemplate.convertAndSend("/topic/public", new WebSocketMessage(message));
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package org.springframework.samples.petclinic.model.common;
|
||||
|
||||
import org.springframework.samples.petclinic.model.Person;
|
||||
|
||||
public class User extends Person {
|
||||
|
||||
public User() {
|
||||
super();
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.springframework.samples.petclinic.model.common;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class WebSocketMessage {
|
||||
|
@ -11,7 +10,6 @@ public class WebSocketMessage {
|
|||
|
||||
private String content;
|
||||
|
||||
|
||||
public WebSocketMessage(String content) {
|
||||
this.time = LocalDate.now().toString();
|
||||
this.content = content;
|
||||
|
@ -40,4 +38,5 @@ public class WebSocketMessage {
|
|||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.springframework.samples.petclinic.model.common;
|
||||
|
||||
public enum WebSocketMessageType {
|
||||
MESSAGE,
|
||||
CONNECT,
|
||||
DISCONNECT
|
||||
|
||||
MESSAGE, CONNECT, DISCONNECT
|
||||
|
||||
}
|
||||
|
|
|
@ -6,3 +6,7 @@ nonNumeric=must be all numeric
|
|||
duplicateFormSubmission=Duplicate form submission is not allowed
|
||||
typeMismatch.date=invalid date
|
||||
typeMismatch.birthDate=invalid date
|
||||
english=English
|
||||
french=French
|
||||
german=German
|
||||
spanish=Spanish
|
||||
|
|
|
@ -6,3 +6,7 @@ nonNumeric=darf nur numerisch sein
|
|||
duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt
|
||||
typeMismatch.date=ungültiges Datum
|
||||
typeMismatch.birthDate=ungültiges Datum
|
||||
english=Englisch
|
||||
french=Französisch
|
||||
german=Deutsche
|
||||
spanish=Spanisch
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
welcome=Bienvenido
|
||||
required=Es requerido
|
||||
required=es requerido
|
||||
notFound=No ha sido encontrado
|
||||
duplicate=Ya se encuentra en uso
|
||||
nonNumeric=Sólo debe contener numeros
|
||||
duplicateFormSubmission=No se permite el envío de formularios duplicados
|
||||
typeMismatch.date=Fecha invalida
|
||||
typeMismatch.date=fecha invalida
|
||||
typeMismatch.birthDate=Fecha invalida
|
||||
english=Ingl\u00E9s
|
||||
french=Franc\u00E9s
|
||||
german=Alemán
|
||||
spanish=Espa\u00F1ol
|
||||
|
|
12
src/main/resources/messages/messages_fr.properties
Normal file
12
src/main/resources/messages/messages_fr.properties
Normal file
|
@ -0,0 +1,12 @@
|
|||
welcome=Bienvenue
|
||||
required=est requis
|
||||
notFound=n'a pas \u00E9t\u00E9 trouv\u00E9
|
||||
duplicate=d\u00E9jà utilis\u00E9
|
||||
nonNumeric=doit être num\u00E9rique
|
||||
duplicateFormSubmission=La duplication de formulaire n'est pas autoris\u00E9e
|
||||
typeMismatch.date=date non valide
|
||||
typeMismatch.birthDate=date non valide
|
||||
english=Anglais
|
||||
french=Fran\u00E7ais
|
||||
german=Allemand
|
||||
spanish=Espagnol
|
7
src/main/resources/static/css/style.css
Normal file
7
src/main/resources/static/css/style.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
#footer {
|
||||
position:absolute;
|
||||
bottom:0;
|
||||
width:100%;
|
||||
height:60px; /* Height of the footer */
|
||||
background:#6cf;
|
||||
}
|
62
src/main/resources/static/js/websocket.js
Normal file
62
src/main/resources/static/js/websocket.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
var stompClient = null;
|
||||
|
||||
function setConnected(connected) {
|
||||
$("#connect").prop("disabled", connected);
|
||||
$("#disconnect").prop("disabled", !connected);
|
||||
if (connected) {
|
||||
$("#conversation").show();
|
||||
}
|
||||
else {
|
||||
$("#conversation").hide();
|
||||
}
|
||||
$("#userinfo").html("");
|
||||
}
|
||||
|
||||
function displayMessage() {
|
||||
var socket = new SockJS('/websocket');
|
||||
stompClient = Stomp.over(socket);
|
||||
stompClient.connect({}, function (frame) {
|
||||
setConnected(true);
|
||||
stompClient.subscribe('/topic/public', function (greeting) {
|
||||
showGreeting(JSON.parse(greeting.body).content);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function connect() {
|
||||
var socket = new SockJS('/websocket');
|
||||
stompClient = Stomp.over(socket);
|
||||
stompClient.connect({}, function (frame) {
|
||||
setConnected(true);
|
||||
console.log('Connected: ' + frame);
|
||||
stompClient.subscribe('/topic/public', function (greeting) {
|
||||
$("#userinfo").text(JSON.parse(greeting.body).content);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (stompClient !== null) {
|
||||
stompClient.disconnect();
|
||||
}
|
||||
setConnected(false);
|
||||
console.log("Disconnected");
|
||||
}
|
||||
|
||||
function sendName() {
|
||||
stompClient.send("/app/user", {}, JSON.stringify({'name': $("#name").val()}));
|
||||
}
|
||||
|
||||
function showGreeting(message) {
|
||||
$("#userinfo").text(message);
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$("form").on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
$( "#connect" ).click(function() { connect(); });
|
||||
$( "#disconnect" ).click(function() { disconnect(); });
|
||||
$( "#send" ).click(function() { sendName(); });
|
||||
});
|
|
@ -18,6 +18,7 @@
|
|||
<![endif]-->
|
||||
|
||||
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
|
||||
<link rel="stylesheet" th:href="@{/resources/static/css/style.css}" />
|
||||
|
||||
</head>
|
||||
|
||||
|
@ -78,6 +79,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="container xd-container">
|
||||
|
||||
|
@ -86,18 +89,50 @@
|
|||
<br />
|
||||
<br />
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12 text-center">
|
||||
<img src="../static/resources/images/spring-pivotal-logo.png"
|
||||
th:src="@{/resources/images/spring-pivotal-logo.png}" alt="Sponsored by Pivotal" /></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="navbar" style="position:absolute; bottom:0; width:100%;">
|
||||
<div id="main-content" class="container">
|
||||
<div class="row">
|
||||
<form class="form-inline">
|
||||
<div class="form-group pull-left" id="conversation">
|
||||
<span id="messageOutput" class="form-control" style="width:200px; "></span>
|
||||
</div>
|
||||
<div class="form-group pull-right" >
|
||||
<img src="../static/resources/images/spring-pivotal-logo.png"
|
||||
th:src="@{/resources/images/spring-pivotal-logo.png}" alt="Sponsored by Pivotal" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
|
||||
<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
|
||||
<script th:src="@{/webjars/jquery-ui/jquery-ui.min.js}"></script>
|
||||
<script th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
|
||||
<script th:src="@{/webjars/sockjs-client/sockjs.min.js}"></script>
|
||||
<script th:src="@{/webjars/stomp-websocket/stomp.min.js}"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready( function(){
|
||||
var socket = new SockJS('/websocket');
|
||||
var stompClient = Stomp.over(socket);
|
||||
|
||||
stompClient.connect({}, function (frame) {
|
||||
stompClient.subscribe('/topic/public', function (socketMessage) {
|
||||
|
||||
var message = JSON.parse(socketMessage.body).content;
|
||||
alert(message);
|
||||
$('#messageOutput').text(message);
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
|
Loading…
Reference in a new issue