Spring WebSockets join another chat room - java

I have implemented a console application using Spring and WebSockets. The application works fine if one or more participants are connected to the base method which is anotated like this.
#MessageMapping("/chat")
#SendTo("/topic/messages")
I will copy the configuration and the implementation which i have made o be more clear.
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
#Controller
public class ChatController {
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(#Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
#MessageMapping("/chat/{room}")
#SendTo("/topic/messages/{room}")
public OutputMessage enableChatRooms(#DestinationVariable String room, #Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
}
#Service
public class SessionHandlerService extends StompSessionHandlerAdapter {
private String nickName;
public SessionHandlerService() {
this.nickName = "user";
}
private void sendJsonMessage(StompSession session) {
ClientMessage msg = new ClientMessage(nickName, " new user has logged in.");
session.send("/app/chat", msg);
}
#Override
public Type getPayloadType(StompHeaders headers) {
return ServerMessage.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
System.err.println(payload.toString());
}
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/messages", new SessionHandlerService());
sendJsonMessage(session);
}
}
The problem which i face is that when i subscribe to /topic/messages and session.send("/app/chat", msg);everything works fine. But if i choose something like session.send("/app/chat/room1", msg); and /topic/messages/room1 the participans can not see each other messages like they are in different chat rooms.

Related

How to identify the owner of Session - Spring-Boot Websockets

I am using websockets without Stomp. What is the correct way to decide to whom USer WebSocketSession belongs to?
In my WsConfig i use:
#Configuration
#EnableWebSocket
public class WebSocketServerConfiguration implements WebSocketConfigurer {
protected final CustomWebSocketHandler webSocketHandler;
#Autowired
public WebSocketServerConfiguration(CustomWebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
#SuppressWarnings("NullableProblems")
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws")
.addInterceptors();
}
}
my WsHandler is currently empty:
#Service
public class SplitBillWebSocketHandler extends TextWebSocketHandler {
#Override
public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception {
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//determine User for session
User user = determineUser(session);
sessionStorage.add(user.getName(),session);
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage jsonTextMessage) throws Exception {
}
}
What is the way to determine the user? Or what is the best practice to do this?
Do I need to pass some parameter to websocket URL from client ( which isn't standard as far as I am aware ), or how to identify the session?
Thanks for help!

Java Flow.Subscriber - How can I get the publisher of onComplete?

I'm creating an user event system using JDK 9 Flow API, so I have a room (which extends the UserSubscriver class above), it may have many users and each user can offer (dispatch) updates at any time.
public abstract class UserSubscriver implements Flow.Subscriber<Notification> {
private Flow.Subscription subscription;
#Override
public void onSubscribe(final Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
#Override
public void onError(final Throwable throwable) {
// ...
}
#Override
public void onNext(final Notification notification) {
// ...
subscription.request(1);
}
#Override
public void onComplete() {
// How can I know who was the publisher of this?
}
}
User class:
public class User extends SubmissionPublisher<Notification> {
....
public int offer(Notification item) {
return super.offer(item, (sub, msg) -> false);
}
}
On the onUpdate I can receive any args, so I can receive the publisher of the update, but there are no args on onComplete.
How can I know who was the publisher of an onComplete event?

How to send binary with spring boot websocket?

In other spring platform , I used websocket to send binary message,like this:
ByteBuffer bf = ByteBuffer.wrap(bytes);
old.getBasicRemote().sendBinary(bf);
But with spring-boot, I make my class extends TextWebSocketHandler. But in the method handleTextMessage(WebSocketSession session, TextMessage message) , only have param WebSocketSession and it has no method to send binary.
I try to use BinaryMessage,like this:
session.sendMessage(new BinaryMessage(bytes));
But the client side get the result as Blob(js type), I don't what to do...
you can use BinaryWebSocketHandler to handle binary message communication.
full example
#Configuration
#EnableAutoConfiguration
#EnableWebSocket
public class AppWebSocket {
public static void main(String[] args) {
SpringApplication.run(AppWebSocket.class, args);
}
#Component
public static class MyWebSocketConfigurer implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyTextHandler(), "/text").withSockJS();
registry.addHandler(new MyBinaryHandler(), "/binary").withSockJS();
}
}
#Component
public static class MyTextHandler extends TextWebSocketHandler {
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(new TextMessage("hello world"));
}
}
#Component
public static class MyBinaryHandler extends BinaryWebSocketHandler {
public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
try {
session.sendMessage(new BinaryMessage("hello world".getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Spring websocket - Activemq listener integration

I am using spring 4.0.0.rc1 websocket endpoint and topic.
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
#ComponentScan(basePackages="org.springframework.samples")
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocketendpoint").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerConfigurer configurer) {
configurer.enableSimpleBroker("/queue/", "/topic/");
}
}
But JMS listener can not post a message on websocket-topic. In this case I am using camel-consume annotation to listen to queue and post message to topic.
#Service
public class WebsocketTopicService implements ApplicationListener<BrokerAvailabilityEvent>
{
private static final Logger log = Logger.getLogger(MessageChannelService.class);
private final MessageSendingOperations<String> messagingTemplate;
private AtomicBoolean brokerAvailable = new AtomicBoolean();
#Autowired
public MessageChannelService(MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#Override
public void onApplicationEvent(BrokerAvailabilityEvent event) {
this.brokerAvailable.set(event.isBrokerAvailable());
}
//#Scheduled(fixedDelay=2000)
#Consume(uri="activemq:queue.out")
public void processMessage(String msg){
if (this.brokerAvailable.get()) {
this.messagingTemplate.convertAndSend("/topic/message.status", msg);
log.info("Sending quote " + msg);
}
}
Any idea why ??

How do I access instantiated WebSockets in Jetty 9?

This is probably obvious, but I am new to this paradigm. I create a Jetty Server and register my websocket class as follows:
Server server = new Server(8080);
WebSocketHandler wsHandler = new WebSocketHandler()
{
#Override
public void configure(WebSocketServletFactory factory)
{
factory.register(MyEchoSocket.class);
}
};
server.setHandler(wsHandler);
The websocket receives messages fine. I would like to also be able to send messages out from the server without having first received a message from the client. How do I access the MyEchoSocket instance that's created when the connection opens? Or, more generally, how do I send messages on the socket outside of the onText method in MyEchoSocket?
Two common techniques, presented here in a super simplified chatroom concept.
Option #1: Have WebSocket report back its state to a central location
#WebSocket
public class ChatSocket {
public Session session;
#OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
ChatRoom.getInstance().join(this);
}
#OnWebSocketMessage
public void onText(String message) {
ChatRoom.getInstance().writeAllMembers("Hello all");
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
ChatRoom.getInstance().part(this);
}
}
public class ChatRoom {
private static final ChatRoom INSTANCE = new ChatRoom();
public static ChatRoom getInstance()
{
return INSTANCE;
}
private List<ChatSocket> members = new ArrayList<>();
public void join(ChatSocket socket)
{
members.add(socket);
}
public void part(ChatSocket socket)
{
members.remove(socket);
}
public void writeAllMembers(String message)
{
for(ChatSocket member: members)
{
member.session.getRemote().sendStringByFuture(message);
}
}
public void writeSpecificMember(String memberName, String message)
{
ChatSocket member = findMemberByName(memberName);
member.session.getRemote().sendStringByFuture(message);
}
public ChatSocket findMemberByName(String memberName)
{
// left as exercise to reader
}
}
Then simply use the central location to talk to the websockets of your choice.
ChatRoom.getInstance().writeSpecificMember("alex", "Hello");
// or
ChatRoom.getInstance().writeAllMembers("Hello all");
Option #2: Have WebSocket be created manually with WebSocketCreator
#WebSocket
public class ChatSocket {
public ChatRoom chatroom;
public ChatSocket(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
#OnWebSocketConnect
public void onConnect(Session session) {
chatroom.join(this);
}
#OnWebSocketMessage
public void onText(String message) {
chatroom.writeAllMembers(message);
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
chatroom.part(this);
}
}
public class ChatCreator implements WebSocketCreator
{
private ChatRoom chatroom;
public ChatCreator(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
public Object createWebSocket(UpgradeRequest request,
UpgradeResponse response)
{
// We want to create the Chat Socket and associate
// it with our chatroom implementation
return new ChatSocket(chatroom);
}
}
public class ChatHandler extends WebSocketHandler
{
private ChatRoom chatroom = new ChatRoom();
#Override
public void configure(WebSocketServletFactory factory)
{
factory.setCreator(new ChatCreator(chatroom));
}
}
At this point you can use the same techniques as above to talk to the websockets of your choice.

Categories