Question : Is it possible to send data from server to client in spring ,using websocket, without creating another client , in the java app that sustains the server ?
Almost every article on the internet about websocket in spring, shows how to define your methods in the controller for handling requests . All the magic seem to happen when they define a function with the 2 annotations #MessageMapping("/news") , #SendTo("/topic/news") . By having this method alone , as far as I know, you can only catch requests and send them further ( SendTo) . I want to know if it is possible to send data from server to client without having a client requesting something in the first place. And how that code would look like.
you could use SimpMessagingTemplate .
i think you should have this class somewhere
#Configuration
#EnableWebSocketMessageBroker
public class "yournameclass" extends AbstractWebSocketMessageBrokerConfigurer
then this method inside
#Override
public void configureMessageBroker(MessageBrokerRegistry configuration) {
configuration.enableSimpleBroker("/test");
configuration.setApplicationDestinationPrefixes("/someprefix");
}
then you could call this from some method in your code
#Autowired
private SimpMessagingTemplate template;
public void "mymethodsender" ()
{ this.template.convertAndSend("/test/somepathwhereyouregisteredyourclienttoreceivemessages", "messageobject");
}
Related
I'm writing simple Stomp Websocket application with Spring, and clients are both web (JS), and Mobile (ios, android). From JS code client connecting over SockJS, while mobile clients are using plain websocket connection behind SockJS.
The issue is that behaviour in my ChannelInterceptor where I'm checking authentication, is completely different for different type of connections. I can't make it work the same for every client.
Let me briefly give some code behind it and explain by example:
Websocket starter was taken from Spring example here: https://github.com/spring-guides/gs-messaging-stomp-websocket.git
Websocket Config:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new MyChannelInterceptor());
}
}
And ChannelInterceptor itself:
public class MyChannelInterceptor implements ChannelInterceptor {
#Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
StompCommand command = accessor.getCommand();
...
}
}
When I'm connecting via SockJS from JS app (http://localhost:8080/gs-guide-websocket - and let Spring SockJS do the rest):
I can catch CONNECT command in MyChannelInterceptor, in postSend method - OK
When I close the connection, in the same place DISCONNECT command fires TWICE. - Not OK
When I'm connecting via Websocket behind SockJS (ws://localhost:8080/gs-guide-websocket/websocket):
I CAN'T catch CONNECT command in MyChannelInterceptor, in postSend method - CRITICAL
When I close the connection, DISCONNECT command fires correctly, once. - OK
Basically, though I can't understand why sockjs tries to disconnect twice, I can live with it.
But with interceptor not catching every connect event - I can't live, since I'm going to keep track of user session, and store them from exactly that interceptor.
I've already tried to remove .withSockJs() in the config - and just connect to socket - same problem
I've also tried to implement application event listener on SessionConnectEvent and SessionConnectedEvent - same problem
Now I'm completely stuck and don't know where else I can go from here...
Any help or starting point is appreciated.
Thanks a lot for any attention to my pain =(
After posting an issue to Spring Github and conversating there, I found out that this is not a bug, and basically not an issue, but just my fault:
The behavior for DISCONNECT is expected. It is mentioned in several places in the Spring WebSocket documentation, under Interception, Events, and Monitoring chapters.
CONNECT event is not expected to be fired when connecting via plain Websockets, cause it is just establishing connecting over plain WebSocket protocol, and for STOMP events you need to use STOMP client.
For those interested, please refer to the corresponding thread:
https://github.com/spring-projects/spring-framework/issues/24269
I'm trying to run example from http://www.baeldung.com/spring-remoting-amqp, even when I set up the connection to the dedicated vhost to my RabbitMQ broker, I can only send the request from client (I see it in RabbitMQ UI), but I never get the answer from the server.
The server seems to bean the service (the returning Impl class) with getBeanDefinitionNames(), but I definitly do not see those beans on the client side. I use annotations to set up beans, not the .xml file.
So the question is - why my client is not seeing the Server beans, I discover it more a less in following way:
#Autowired
private ApplicationContext appContext;
public GetResponse get(String id) {
Service service = appContext.getBean(Service.class);
System.out.println(service.ping());
return new GetResponse();
}
The answer which I get on the level of webservice is:
{
"timestamp": "2018-02-01T10:09:00.809Z",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.remoting.RemoteProxyFailureException",
"message": "No reply received from 'toString' with arguments '[]' - perhaps a timeout in the template?",
"path": "/v3/app/r"
}
Service:
public interface Service extends Serializable{
String ping();
}
Service Impl:
public class ServiceImpl implements Service {
#Override
public String ping() {
System.out.println("ponged");
return "pong";
}
#Override
public String toString() {
return "to string";
}
EDITED + BOUNTY
In the link you can find extracted modules which I want to connect together. I suppose that it is still about 'not seeing' the beans from one module in the second one.
The action can be trigerd with GET http://localhost:8081/v3/app/u The RabbitMQ settings has to be adjusted to your set-up.
https://bitbucket.org/herbatnic/springremotingexample/overview
I think you shouldn't set the routing key in your client, in amqpFactoryBean (and the one you set seems invalid):
https://bitbucket.org/herbatnic/springremotingexample/src/b1f08a5398889525a0b1a439b9bb4943f345ffd1/Mod1/src/main/java/simpleremoting/mod1/messaging/Caller.java?at=master&fileviewer=file-view-default
Did you try to run their example?
https://github.com/eugenp/tutorials/tree/master/spring-remoting/remoting-amqp
Just stumbled upon this question 3 years later.. trying to run the Baeldung example!
I tried debugging the issue and as far as I can tell, something internal in the AMQP implementation of spring remoting is not using the correct Routing Key when sending the client message, meaning the payload arrives at the broker and is never put into the queue for processing, we then timeout after 5s (default) on the client.
I tried the other answer by Syl to remove the routingKey however it doesn't seem to allow us to create a binding without one, and even when creating a binding directly on the broker management page (without a routing key) it doesn't route the messages.
I have not managed to make the example work, however I found a blog post on fatalerrors.org that shows a custom implementation of the AmqpProxyFactoryBean and it has custom handling for the routing key, this one works.
I've create this gist with the example that is working for me in case the blog post above goes under.
One other thing to note is that on the Baeldung example they are using a DirectExchange, while here we are using a TopicExchange.
I want to create a simple news feed feature on the front end that will automatically update through websocket push notifications.
The technologies involved are:
Angular for the general front-end application
SockJS for creating websocket communication
Stomp over webosocket for receiving messages from a message broker
Springboot Websockets
Stomp Message Broker (the java related framework)
What I want to achieve on the front end is:
Create a websocket connection when the view is loaded
Create s stomp provider using that websocket
Have my client subscribe to it
Catch server pushed messages and update the angular view
As far as the server side code:
Configure the websocket stuff and manage the connection
Have the server push messages every X amount of time (through an executor or #Scheduled?).
I think I have achieved everything so far except the last part of the server side code. The example I was following uses the websocket in full duplex mode and when a client sends something then the server immediately responds to the message queue and all subscribed clients update. But what I want is for the server itself to send something over Stomp WITHOUT waiting for the client to make any requests.
At first I created a spring #Controller and added a method to it with #SendTo("/my/subscribed/path") annotation. However I have no idea how to trigger it. Also I tried adding #Scheduled but this annotation works only on methods with void return type (and I'm returning a NewsMessage object).
Essentially what I need is to have the client initialize a websocket connection, and after have the server start pushing messages through it at a set interval (or whenever an event is triggered it doesn't matter for now). Also, every new client should listen to the same message queue and receive the same messages.
Before starting, make sure that you have the websocket dependencies in your pom.xml. For instance, the most important one:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${org.springframework-version}</version>
</dependency>
Then, you need to have your configuration in place. I suggest you start with simple broker.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic", "/queue");
}
}
Then your controller should look like this. When your AngularJs app opens a connection on /portfolio and sends a subscription to channel /topic/greeting, you will reach the controller and respond to all subscribed users.
#Controller
public class GreetingController {
#MessageMapping("/greeting")
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
}
With regard to your scheduler question, you need to enable it via configuration:
#Configuration
#EnableScheduling
public class SchedulerConfig{}
And then schedule it:
#Component
public class ScheduledUpdatesOnTopic{
#Autowired
private SimpMessagingTemplate template;
#Autowired
private final MessagesSupplier messagesSupplier;
#Scheduled(fixedDelay=300)
public void publishUpdates(){
template.convertAndSend("/topic/greetings", messagesSupplier.get());
}
}
Hope this somehow clarified the concept and steps to be taken to make things work for you.
First of all you can't send (push) messages to clients without their subscriptions.
Secondly to send messages to all subscribers you should take a look to the topic abstraction side.
That is a fundamentals of STOMP.
I think you are fine with #Scheduled, but you just need to inject SimpMessagingTemplate to send messages to the STOMP broker for pushing afterwards.
Also see Spring WebSockets XML configuration not providing brokerMessagingTemplate
I am trying to implement a web socket through servlet. My app server is tomcat 7.
I could find examples, where WebSocketServlet class is used. But this class is deprecated and removed in tomcat 8.
I see another alternative, which is to annotate the class with the following
#ServerEndpoint(value = "/websocket/test")
I need helpin understanding,
How will I use this annotation in servlets? Are servlets irrelevent in case of web sockets?
If I create a normal class with the above annotation, and other annotation like onOpen,onClose etc, should I need to put the entry for
that class in web.xml? Or are web.xmls are irrelevant too?
Any hello world link will also be very helpful.
Thank you.
============Edited====================
I have tried the chat example found in this link
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/
But when I try to invoke the socket through javascript, the events are not reaching my server at all....
Finally I figured this out. So I am answering here for others to refer.
1)How will I use this annotation in servlets? Are servlets irrelevent in case of web sockets?
Apparently yes, we don't need servlets for web sockets.
2)If I create a normal class with the above annotation, and other annotation like onOpen,onClose etc, should I need to put the entry for that class in web.xml? Or are web.xmls are irrelevant too?
No entry needed in web.xml either.
Following, is a sample server side code.
#ServerEndpoint(value = "/echo")
public class Echo {
#OnOpen
public void start(Session session) {
//TODO
}
#OnClose
public void end() {
//TODO
}
#OnMessage
public void incoming(String message) {
//TODO
}
#OnError
public void onError(Throwable t) throws Throwable {
//TODO
}
}
For client, either you can use Javascript is you have a HTML5 compatible browser.
Else you write java clients using the tyrus library. Refer here
The question might seem stupid/trivial and might be, but I simply cannot understand how to achieve my goal. (Sorry if the title is misguiding, couldn't think of a better one)
I have a webpage on a App Engine server which uses GWT. I got client code and server code. The client code can call RPC methods without any problem (my problem has nothing to do with the "gwt-client" at all).
I got the following classes:
//MyClassService.java - client package
#RemoteServiceRelativePath("myService")
public interface MyClassService extends RemoteService{
public doSomething();
}
//MyClassServiceAsync.java - client package
public interface MyClassServiceAsync{
public void doSomething(AsyncCallback<Void> callback);
}
//MyClassServiceImpl.java - server package
public class MyClassServiceImpl extends RemoteServiceServlet implements MyClassService{
#Override
public void doSomething()
{
//does something
}
}
A scenario and what I want to do:
I've got a remote client, in other words, a client who's not connecting through the page via the "GWT-interface", it's a client who's simply making GET, POST requests to a path on the server (from elsewhere). This remote client is not "using" GWT at all. The client is connecting through an HttpServlet, inside this servlet I want to reuse the RPC mechanics so that i don't have to rewrite the interfaces, who are on the client side and using client-dependent code (the implementation is already server-side).
To reuse the existing methods on the server-side I could create an instance of MyClassServiceImpl.java and just use those. BUT as you can see above, they are implemented as synchronous methods, since GWT-RPC automatically makes the calls asyncronous when using the GWT-RPC.
How would i go about to reuse the MyClassServiceImpl on the server-side and also get them as asynchronous?
Also if I'm wrong with the approach I'm taking, please suggest some other solution. For example, one solution might be for the remote client to directly communicate with the RemoteServiceServlet instead of creating a HttpServlet which the client connects through, but I don't know if that's possible (and if it is, please tell me how)!
Thank you!
EDIT (thanks to some answers below I got some insight and will try to improve my question):
The server-side implementation of the methods is SYNCHRONOUS. Meaning they will block until results a returned. When invoking these method from the gwt-client code, they are 'automatically' made ASYNCHRONOUS one can call them by doing the following:
MyClassServiceAsync = (MyClassServiceAsync) GWT.create(MyClassService.class);
ServiceDefTarget serviceDef = (ServiceDefTarget) service;
serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "myService");
service.doSomething(new AsyncCallback<Void>() {
#Override
public void onSuccess(Void result) {
//do something when we know server has finished doing stuff
}
#Override
public void onFailure(Throwable caught) {
}
});
As you can see from the above code, there is support for the doSomething method to take an AsyncCallback, without even having the implementation for it. This is what I wanted on the server-side so i didn't have to use threads or create a new implementation for "async-usage". Sorry if I was unclear!
1) Any client can call MyClassServiceImpl.doSomething() with the current configuration. MyClassServiceImpl is a servlet and properly exposed. In order to achieve communication this way, the client must be able to "speak" the GWT dialect for data transportation. Google may provide you with libraries implementing this. I haven't used any, so I cannot make suggestions.
An example, proof-of-concept setup: Check the network communications with Firebug to get an idea of what is going on. Then try calling the service with curl.
2) If you do not want to use the GWT dialect, you can easily expose the same service as REST (JSON) or web services (SOAP). There are plenty of libraries, e.g. for the REST case RestEasy and Jersey. You do not mention any server-side frameworks (Spring? Guice? CDI?), so the example will be simplistic.
I'd suggest implementing your business method in a class independent of transportation method:
public class MyBusinessLogic {
public void doSomething() {
...
}
}
Then, the transport implementations use this business logic class, adding only transport-specific stuff (e.g. annotations):
GWT:
public class MyClassServiceImpl extends RemoteServiceServlet implements MyClassService{
#Override
public void doSomething() {
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}
JAX-RS:
#Path("myService")
public class MyResource {
#GET
public void doSomething() {
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}
So the transport endpoints are just shells for the real functionality, implemented in one place, the class MyBusinessLogic.
Is this a real example? Your method takes no arguments and returns no data.
Anyhow you can create a new servlet and invoke it via normal HTTP request. The servlet then just invokes the target method:
public class MyNewServlet extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response){
MyBusinessLogic bean = ... // get it from IoC, new, whatever
bean.doSomething();
}
}