Last active
December 30, 2023 04:15
-
-
Save theotherian/9906304 to your computer and use it in GitHub Desktop.
Spring STOMP chat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Spring STOMP chat |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function showActive(activeMembers) { | |
renderActive(activeMembers.body); | |
stompClient.send('/app/activeUsers', {}, ''); | |
} | |
function renderActive(activeMembers) { | |
var previouslySelected = $('.user-selected').text(); | |
var usersWithPendingMessages = new Object(); | |
$.each($('.pending-messages'), function(index, value) { | |
usersWithPendingMessages[value.id.substring(5)] = true; // strip the user- | |
}); | |
var members = $.parseJSON(activeMembers); | |
var userDiv = $('<div>', {id: 'users'}); | |
$.each(members, function(index, value) { | |
if (value === whoami) { | |
return true; | |
} | |
var userLine = $('<div>', {id: 'user-' + value}); | |
userLine.addClass('user-entry'); | |
if (previouslySelected === value) { | |
userLine.addClass('user-selected'); | |
} | |
else { | |
userLine.addClass('user-unselected'); | |
} | |
var userNameDisplay = $('<span>'); | |
userNameDisplay.html(value); | |
userLine.append(userNameDisplay); | |
userLine.click(function() { | |
var foo = this; | |
$('.chat-container').hide(); | |
$('.user-entry').removeClass('user-selected'); | |
$('.user-entry').addClass('user-unselected'); | |
userLine.removeClass('user-unselected'); | |
userLine.removeClass('pending-messages'); | |
userLine.addClass('user-selected'); | |
userLine.children('.newmessage').remove(); | |
var chatWindow = getChatWindow(value); | |
chatWindow.show(); | |
}); | |
if (value in usersWithPendingMessages) { | |
userLine.append(newMessageIcon()); | |
userLine.addClass('pending-messages'); | |
} | |
userDiv.append(userLine); | |
}); | |
$('#userList').html(userDiv); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import java.security.Principal; | |
import javax.inject.Inject; | |
import org.springframework.messaging.Message; | |
import org.springframework.messaging.handler.annotation.MessageMapping; | |
import org.springframework.messaging.simp.SimpMessageHeaderAccessor; | |
import org.springframework.stereotype.Controller; | |
@Controller | |
public class ActiveUserController { | |
private ActiveUserService activeUserService; | |
@Inject | |
public ActiveUserController(ActiveUserService activeUserService) { | |
this.activeUserService = activeUserService; | |
} | |
@MessageMapping("/activeUsers") | |
public void activeUsers(Message<Object> message) { | |
Principal user = message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER, Principal.class); | |
activeUserService.mark(user.getName()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import java.util.Set; | |
import org.springframework.messaging.simp.SimpMessagingTemplate; | |
import org.springframework.scheduling.annotation.Scheduled; | |
public class ActiveUserPinger { | |
private SimpMessagingTemplate template; | |
private ActiveUserService activeUserService; | |
public ActiveUserPinger(SimpMessagingTemplate template, ActiveUserService activeUserService) { | |
this.template = template; | |
this.activeUserService = activeUserService; | |
} | |
@Scheduled(fixedDelay = 2000) | |
public void pingUsers() { | |
Set<String> activeUsers = activeUserService.getActiveUsers(); | |
template.convertAndSend("/topic/active", activeUsers); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import java.util.Set; | |
import java.util.concurrent.atomic.AtomicLong; | |
import com.google.common.cache.CacheBuilder; | |
import com.google.common.cache.CacheLoader; | |
import com.google.common.cache.LoadingCache; | |
import com.google.common.collect.Sets; | |
public class ActiveUserService { | |
private LoadingCache<String, UserStats> statsByUser = CacheBuilder.newBuilder().build(new CacheLoader<String, UserStats>() { | |
@Override | |
public UserStats load(String key) throws Exception { | |
return new UserStats(); | |
} | |
}); | |
public void mark(String username) { | |
statsByUser.getUnchecked(username).mark(); | |
} | |
public Set<String> getActiveUsers() { | |
Set<String> active = Sets.newTreeSet(); | |
for (String user : statsByUser.asMap().keySet()) { | |
// has the user checked in within the last 5 seconds? | |
if ((System.currentTimeMillis() - statsByUser.getUnchecked(user).lastAccess()) < 5000) { | |
active.add(user); | |
} | |
} | |
return active; | |
} | |
private static class UserStats { | |
private AtomicLong lastAccess = new AtomicLong(System.currentTimeMillis()); | |
private void mark() { | |
lastAccess.set(System.currentTimeMillis()); | |
} | |
private long lastAccess() { | |
return lastAccess.get(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.context.annotation.ComponentScan; | |
/** | |
* Set up Spring boot and launch the application | |
*/ | |
@ComponentScan | |
@EnableAutoConfiguration | |
public class Application { | |
public static void main(String[] args) { | |
SpringApplication.run(Application.class, args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
public class ChatMessage { | |
private String recipient; | |
public String getRecipient() { return recipient; } | |
public void setRecipient(String recipient) { this.recipient = recipient; } | |
private String sender; | |
public String getSender() { return sender; } | |
public void setSender(String sender) { this.sender = sender; } | |
private String message; | |
public String getMessage() { return message; } | |
public void setMessage(String message) { this.message = message; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import javax.inject.Inject; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.messaging.simp.SimpMessagingTemplate; | |
import org.springframework.scheduling.annotation.EnableScheduling; | |
import org.springframework.scheduling.annotation.SchedulingConfigurer; | |
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; | |
import org.springframework.scheduling.config.ScheduledTaskRegistrar; | |
/** | |
* Override the scheduling configuration so that we can schedule our own scheduled bean and | |
* so that Spring's STOMP scheduling can continue to work | |
*/ | |
@Configuration | |
@EnableScheduling | |
public class ChatSchedulingConfigurer implements SchedulingConfigurer { | |
@Bean | |
public ThreadPoolTaskScheduler taskScheduler() { | |
return new ThreadPoolTaskScheduler(); | |
} | |
/** | |
* This is setting up a scheduled bean which will see which users are active | |
*/ | |
@Bean | |
@Inject | |
public ActiveUserPinger activeUserPinger(SimpMessagingTemplate template, ActiveUserService activeUserService) { | |
return new ActiveUserPinger(template, activeUserService); | |
} | |
@Override | |
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { | |
taskRegistrar.setTaskScheduler(taskScheduler()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function connect() { | |
socket = new SockJS('/chat'); | |
stompClient = Stomp.over(socket); | |
stompClient.connect('', '', function(frame) { | |
whoami = frame.headers['user-name']; | |
console.log('Connected: ' + frame); | |
stompClient.subscribe('/user/queue/messages', function(message) { | |
showMessage(JSON.parse(message.body)); | |
}); | |
stompClient.subscribe('/topic/active', function(activeMembers) { | |
showActive(activeMembers); | |
}); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function showMessage(message) { | |
var chatWindowTarget = (message.recipient === whoami) ? message.sender : message.recipient; | |
var chatContainer = getChatWindow(chatWindowTarget); | |
var chatWindow = chatContainer.children('.chat'); | |
var userDisplay = $('<span>', {class: (message.sender === whoami ? 'chat-sender' : 'chat-recipient')}); | |
userDisplay.html(message.sender + ' says: '); | |
var messageDisplay = $('<span>'); | |
messageDisplay.html(message.message); | |
chatWindow.append(userDisplay).append(messageDisplay).append('<br/>'); | |
chatWindow.animate({ scrollTop: chatWindow[0].scrollHeight}, 1); | |
if (message.sender !== whoami) { | |
var sendingUser = $('#user-' + message.sender); | |
if (!sendingUser.hasClass('user-selected') && !sendingUser.hasClass('pending-messages')) { | |
sendingUser.append(newMessageIcon()); | |
sendingUser.addClass('pending-messages'); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import java.security.Principal; | |
import javax.inject.Inject; | |
import org.springframework.messaging.Message; | |
import org.springframework.messaging.handler.annotation.MessageMapping; | |
import org.springframework.messaging.handler.annotation.Payload; | |
import org.springframework.messaging.simp.SimpMessageHeaderAccessor; | |
import org.springframework.messaging.simp.SimpMessagingTemplate; | |
import org.springframework.stereotype.Controller; | |
@Controller | |
public class MessageController { | |
private SimpMessagingTemplate template; | |
@Inject | |
public MessageController(SimpMessagingTemplate template) { | |
this.template = template; | |
} | |
@MessageMapping("/chat") | |
public void greeting(Message<Object> message, @Payload ChatMessage chatMessage) throws Exception { | |
Principal principal = message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER, Principal.class); | |
String authedSender = principal.getName(); | |
chatMessage.setSender(authedSender); | |
String recipient = chatMessage.getRecipient(); | |
if (!authedSender.equals(recipient)) { | |
template.convertAndSendToUser(authedSender, "/queue/messages", chatMessage); | |
} | |
template.convertAndSendToUser(recipient, "/queue/messages", chatMessage); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function newMessageIcon() { | |
var newMessage = $('<span>', {class: 'newmessage'}); | |
newMessage.html('✉'); | |
return newMessage; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |
import org.springframework.security.config.annotation.web.configuration.*; | |
/** | |
* An extremely basic auth setup for the sake of a demo project | |
*/ | |
@Configuration | |
@EnableWebSecurity | |
public class SecurityConfig extends WebSecurityConfigurerAdapter { | |
@Autowired | |
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { | |
auth.inMemoryAuthentication().withUser("ian").password("ian").roles("USER"); | |
auth.inMemoryAuthentication().withUser("dan").password("dan").roles("USER"); | |
auth.inMemoryAuthentication().withUser("chris").password("chris").roles("USER"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function sendMessageTo(user) { | |
var chatInput = '#input-chat-' + user; | |
var message = $(chatInput).val(); | |
if (!message.length) { | |
return; | |
} | |
stompClient.send("/app/chat", {}, JSON.stringify({ | |
'recipient': user, | |
'message' : message | |
})); | |
$(chatInput).val(''); | |
$(chatInput).focus(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.theotherian.chat; | |
import java.util.List; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.messaging.converter.MessageConverter; | |
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; | |
/** | |
* Set up our websocket configuration, which uses STOMP, and configure our endpoints | |
*/ | |
@Configuration | |
@EnableWebSocketMessageBroker | |
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { | |
@Override | |
public void configureMessageBroker(MessageBrokerRegistry config) { | |
config.enableSimpleBroker("/queue", "/topic"); | |
config.setApplicationDestinationPrefixes("/app"); | |
} | |
@Override | |
public void registerStompEndpoints(StompEndpointRegistry registry) { | |
registry.addEndpoint("/chat", "/activeUsers").withSockJS(); | |
} | |
@Override | |
public void configureClientInboundChannel(ChannelRegistration channelRegistration) { | |
} | |
@Override | |
public void configureClientOutboundChannel(ChannelRegistration channelRegistration) { | |
} | |
@Override | |
public boolean configureMessageConverters(List<MessageConverter> converters) { | |
return true; | |
} | |
@Bean | |
public ActiveUserService activeUserService() { | |
return new ActiveUserService(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function getChatWindow(userName) { | |
var existingChats = $('.chat-container'); | |
var elementId = 'chat-' + userName; | |
var containerId = elementId + '-container'; | |
var selector = '#' + containerId; | |
var inputId = 'input-' + elementId; | |
if (!$(selector).length) { | |
var chatContainer = $('<div>', {id: containerId, class: 'chat-container'}); | |
var chatWindow = $('<div>', {id: elementId, class: 'chat'}); | |
var chatInput = $('<textarea>', {id: inputId, type: 'text', class: 'chat-input', rows: '2', cols: '75', | |
placeholder: 'Enter a message. Something deep and meaningful. Something you can be proud of.'}); | |
var chatSubmit = $('<button>', {id: 'submit-' + elementId, type: 'submit', class: 'chat-submit'}) | |
chatSubmit.html('Send'); | |
chatInput.keypress(function(event) { | |
if (event.which == 13) { | |
var user = event.currentTarget.id.substring(11); // gets rid of 'input-chat-' | |
event.preventDefault(); | |
sendMessageTo(user); | |
} | |
}); | |
chatSubmit.click(function(event) { | |
var user = event.currentTarget.id.substring(12); // gets rid of 'submit-chat-' | |
sendMessageTo(user); | |
}); | |
chatContainer.append(chatWindow); | |
chatContainer.append(chatInput); | |
chatContainer.append(chatSubmit); | |
if (existingChats.length) { | |
chatContainer.hide(); | |
} | |
$('body').append(chatContainer); | |
} | |
return $(selector); | |
} |
How do login page comes from, where is the html for it ?
Where is the login page?
Login page is provided by spring when you enable spring security. You can customize it and put your own login page as well.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
excuse me ,How can configure if i want Unicode font support ?[(Failed to parse TextMessage payload=[SEND cont...],,)] cos i got this error when try others language .