I have chat which mapping to static URL. I need get the opportunity creating rooms for user.
How to inject variable in annotation #ServerEndpoint("/myVariable") when app already running?
class SomeClass{
public void createRoom(User user) {
String name = user.getName();
//...Somehow inject name to annotation #ServerEndpoint("/name")...
}
}
#ServerEndpoint("/chat") //May be replace to #ServerEndpoint(someGetteUserName())
public class ChatEndpoint {
#OnMessage
public void message(String message, Session client)
throws IOException, EncodeException {
for (Session peer : client.getOpenSessions()) {
peer.getBasicRemote().sendText(message);
}
}
}
I don't use Spring this is clear websocket and Glassfish.
Help me create implementation variable injection to annotation. Thank You.
I think that you don't need any injection if you only want to create and handle chat rooms. You just need to handle this by java code independently from your endpoint.
I recommend you to:
Create one websocket server endpoint: #ServerEndpoint("/chat"/{client_id}). This client id pathParam is may serve as a session id.
In ChatEndpoint class, initialize a list of rooms (this list should be static <=> common between all threads).
Create your business methods to handle clients and rooms(create/delete user, create/delete room, subscribe to a room...etc).
Finally, in your chat message try to specify the room destination. This can be very simple if you use JSON format.
message = { ... ,"room": "room1", ... }
Related
Could anyone tell me if the server-side implementation is using stomp WebSocket, is the client also expected to implement stomp?
I am trying to implement a spring boot application and I am confused if I should go with or without stomp implementation. From my research, I understand, if you want to scale the application, it is better to use stomp and embedded broker( RabbitMQ for eg.) as it will handle the sessions, heartbeat etc. instead of an in-memory broker.
The examples available online just shows implementations with and without stomp.
I am basically trying to get different datasets from the table upon client request and write to a WebSocket continuously.
Could anyone please confirm if my understanding so far is correct?
What are the essential things I will have to take care of if I go with stomp + websocket?
Updating the usecase below:
The mobile client would be displaying charts upon user login. There would be links in the left panel for eg. Sales, Discounts etc. which upon clicking, the request will reach server through websocket channel. Server will check the datatype in the request, generate the model using data from DB and write the data to the websocket.
Updating code - v1
MyWebSocketHandler:
#Component
public class MyWebSocketHandler extends TextWebSocketHandler {
Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
DashboardUtil dashboardutil;
#Resource(name = "socketSessionsMap")
private Map<String, WebSocketSession> socketSessionsMap;
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws InterruptedException, IOException {
try {
//Gets the socket session from map and writes a json to that socket - did for testing purpose.
socketSessionsMap.put("session", session);
//String payload = message.getPayload();
String jsonString = dashboardutil.getDataInJSON(); // gets hardcoded json model
session.sendMessage(new TextMessage(jsonString));
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
}
WebSecurityConfig:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
private MyWebSocketHandler myWebSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWebSocketHandler, "/socketHandler").setAllowedOrigins("*").withSockJS();
}
}
Could anyone tell me if the server-side implementation is using stomp
WebSocket, is the client also expected to implement stomp?
You can register multiple handlers in your web socket configuration. So in theory you can provide a handler for STOMP and another one for plain web socket. If you only provide a STOMP handler then the handshake from a standard web socket client will fail.
From my research, I understand, if you want to scale the application,
it is better to use stomp and embedded broker( RabbitMQ for eg.) as it
will handle the sessions, heartbeat etc. instead of an in-memory
broker.
That's correct. STOMP also offers a few more nice features especially the subscription to certain endpoints.
I am basically trying to get different datasets from the table upon
client request and write to a WebSocket continuously.
That's a really simple description ...
You should think about if you need to share sessions across multiple instances or if you need to send messages between web socket sessions.
From your description it sounds like you just accept a web socket connection and continuously push data to the client. If you want to scale this application you can just put a load balancer in front of your instances and you are good to go.
SimpUserRegistry lets you retrieve the details of all authenticated Stomp sessions, is there any such class that will let me iterate over anonymous user sessions?
Like howie described in his answer only non anonymous users will be added to the SimpUserRegistry.
But if you really want to add anonymous users also you just have to sub-class the DefaultHandshakeHandler class and override the determineUser method like stated in the Spring Doc for Version 5.0.0.M1 - Chapter 22. This should also be working for 5.1.5.Release which you are currently on when using Spring Boot 2.1.3.RELEASE:
In some cases it may be useful to assign an identity to a WebSocket session even when the user has not been formally authenticated. For example, a mobile app might assign some identity to anonymous users, perhaps based on geographical location. The do that currently, an application can sub-class DefaultHandshakeHandler and override the determineUser method. The custom handshake handler can then be plugged in (see examples in Section 22.2.4, “Deployment Considerations”).
Here is an answer (Spring websockets without principal) which shows you how you can achieve to create an AnonymousPrincipal and determine it within the custom handshake handler.
And at last you have to add your an instance of your custom handshake handler to your registered endpoint but this is depending on whether you use STOMP or not.
Following are some of the code snippets from StompSubProtocolHandler -
The handleMessageFromClient method adds the user to the stompAuthentications map and publishes a SessionConnectEvent event -
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> webSocketMessage, MessageChannel outputChannel) {
//...
SimpAttributesContextHolder.setAttributesFromMessage(message);
boolean sent = outputChannel.send(message);
if (sent) {
if (isConnect) {
Principal user = headerAccessor.getUser();
if (user != null && user != session.getPrincipal()) {
this.stompAuthentications.put(session.getId(), user);
}else{
//TODO try to handle here for anonymous user
}
}
if (this.eventPublisher != null) {
if (isConnect) {
publishEvent(new SessionConnectEvent(this, message, getUser(session)));
}
//...
I think you have to Check this socure code StompSubProtocolHandler, and customize it.
I have recently started looking into Spring Integration as a result of this question and have a question about object-type based routing.
The application I am maintaining needs to process requests from an incoming ActiveMQ queue (request.queue) and send a response back to the caller on a topic (response.topic). At present the requests are structured as follows:
public abstract class Request {
// base class
}
public abstract class CustomerRequest extends Request {
// base class for customer-specific requests
}
public class FindCustomerByIdRequest extends CustomerRequest {
private int id;
}
public class FindAllCustomersRequest extends CustomerRequest {
private boolean includeArchivedCustomers;
}
public class AddCustomerRequest extends CustomerRequest {
private String name;
private Date signupDate;
private Address address;
}
I have a service for each high level domain object which provides the functionality to service these incoming requests:
#Service
public class CustomerService {
public CustomerResponse findCustomerById(FindCustomerByIdRequest request) {
// code snipped
return customerResponse;
}
public AddCustomerResponse addCustomer(AddCustomerRequest request) {
// code snipped
return addCustomerResponse;
}
}
I need to route each specific request to the approriate method in CustomerService via #ServiceActivator which I understand can be done by creating a separate channel for each request and implementing a PayloadTypeRouter to place requests on the correct channel based on type.
Over time the list of request types is going to grow, and I am questioning whether a one-channel-per-request setup is efficient/practical/scalable. For example, if there are 100 different request types in the future there are going to be 100 different channels.
What would be great is if I could route the high-level requests of superclass CustomerRequest to CustomerService and have Spring work out the approriate method to call via an annotation or some other mechanism. Does anyone know if this is possible, or have any comments regarding the many-channels approach?
If there is no ambiguity, use <service-activator ... reg="fooBean" /> (no method) and the framework will chose the target method based on the payload.
If there is ambiguity (more than one method for the same type), it will fail.
However, a single class with 100+ methods is probably not a good design.
It seems to be that your request types are app feature specific. This suggest that you have one queue for all the possible application features. That is horrible idea. You should have at least separate queue per feature.
I suggest to rethink the design of your app.
Does anybody know how to follow this recommendation at [camel pojo producing][1]:
We recommend Hiding Middleware APIs from your application code so the
next option might be more suitable. You can add the #Produce
annotation to an injection point (a field or property setter) using a
ProducerTemplate or using some interface you use in your business
logic. e.g.
public interface MyListener {
String sayHello(String name);
}
public class MyBean {
#Produce(uri = "activemq:foo")
protected MyListener producer;
public void doSomething() {
// lets send a message
String response = producer.sayHello("James");
}
}
Here Camel will automatically inject a smart client side proxy at the
#Produce annotation - an instance of the MyListener instance. When we
invoke methods on this interface the method call is turned into an
object and using the Camel Spring Remoting mechanism it is sent to the
endpoint - in this case the ActiveMQ endpoint to queue foo; then the
caller blocks for a response.
but with headers, in order to work with this line of DSL:
from("jms:activemq:toBeStored").recipientList(header("stores").tokenize(",")).parallelProcessing().ignoreInvalidEndpoints();
which I have successfully tested with the method of #EndpointInject
public class Foo {
#EndpointInject(uri="activemq:foo.bar")
ProducerTemplate producer;
public void doSomething() {
if (whatever) {
producer.sendBodyAndHeader("<hello>world!</hello>", "stores","store1");
}
}
}
like as described before the recommendation.
In brief: how can I make the injected smart client side proxy send also the headers I want?
thanks
I'm trying to figure out a good way to organize a javax.websocket multiplayer card game I'm working on.
I want to split up my code into multiple classes which are each a ServerEndpoint. My problem is that I need an effective way of sharing session data between them.
I have an index at "/index", which is where I'm currently creating Player objects for clients. I'm setting these like so:
#ServerEndpoint("/index")
public class IndexEndpoint {
#OnOpen
public void openConnection(Session session) {
session.getUserProperties().put("player", new Player());
}
}
And that works; I can access the Player objects elsewhere throughout IndexEndpoint.
But when I try to access the user properties from another endpoint (after having established a connection with IndexEndpoint in JavaScript, waiting 5 seconds and then opening up an additional connection to LobbyEndpoint on the same page), I get null.
#ServerEndpoint("/lobby")
public class LobbyEndpoint {
#OnOpen
public void openConnection(Session session) {
System.out.println(session.getUserProperties().get("player")); // prints null
}
}
Which leads me to imply that session data is unfortunately not shared across endpoints.
Is there any good way for me to share websocket session data across multiple classes?
I guess one solution would be to just have one über endpoint for all users to connect to, and which can handle any message type. Then that endpoint delegates message data to other parts of my application.
However I feel like this design would be very messy and restrictive. I'd like to have endpoints dedicated to specific rooms, using annotations like #ServerEndpoint("/rooms/{room-id}") and #PathParam("room-id"). So if I was using a monolithic Endpoint then I couldn't do that; I wouldn't be able to know if a connecting user is a user who access to the room.
what about a singleton EJB containing all player and game data?
#Stateless
#ServerEndpoint("/lobby/{sessionKey}")
public class IndexEndpoint {
#EJB
GameData data;
#OnOpen
public void onOpen(Session session, EndpointConfig config,
#PathParam("sessionKey") int id) {
Player player = data.getPlayer(id);
session.getUserProperties().put("player", player);
}
}
The first message from the IndexEndpoint could provide the client with its id and then the client could provide its id to the LobbyEndpoint in the ws URL (or in a header with a Configurator), that is how I plan to do it.