im trying to make an application who cominicates with a server made in spring-boot with websocket.
My server side configuration is the tutorial of spring-boot websocket
package ar.com.sourcesistemas.armController.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
This is my welcome controller:
package ar.com.sourcesistemas.armController;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class WelcomeController {
// inject via application.properties
#Value("${welcome.message:test}")
private String message = "Hello World";
#RequestMapping("/")
public String welcome(Map<String, Object> model) {
model.put("message", this.message);
return "welcome";
}
}
and this is the broadcaster:
package ar.com.sourcesistemas.armController;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import ar.com.sourcesistemas.armController.pruebas.Greeting;
import ar.com.sourcesistemas.armController.pruebas.HelloMessage;
#Controller
public class ArmController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
}
to connect to this websocket im using org.asynchttpclient, and my code is this:
AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
String url = "ws://192.168.0.23:8080/gs-guide-websocket/app/topic/greetings";
AsyncHttpClient c = new DefaultAsyncHttpClient();
WebSocket websocket = c.prepareGet(url)
.execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() {
public void onMessage(String message) {
System.out.println(message);
}
public void onOpen(WebSocket websocket) {
System.out.println("impĀ“rimo websocket");
}
public void onClose(WebSocket websocket) {
System.out.println("ni idea");
}
public void onError(Throwable t) {
}
}).build()).get();
Here i have 2 problems , the first one is :
when i run this program i have an error message in the server side it says "Origin cgeck enabled but transport 'greetings' does not support it".
The second one is:
To made a websocket you have 1 endpoint , in this case is "/gs-guide-websocket", and a broker you can subscribe. All data published in that broker will be broadcasted to all programs subscribed to it. In this case that channel to subscribe will be /app/toppic.
I cant figure out how to subscribe my java desktop application to this message broker.
MORE INFO
I can do this with jquery with this code :
<script src="jquery-3.1.1.min.js"></script>
<script src="sockjs.min.js"></script>
<script src="stomp.min.js"></script>
<script type="text/javascript">
var stompClient = null;
function connect() {
var socket = new SockJS('http://192.168.0.23:8080/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
console.log(JSON.parse(greeting.body).content);
});
});
}
function showGreeting(message) {
$("#greetings").append("<tr><td>" + message + "</td></tr>");
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
$( document ).ready(function() {
connect();
});
but i cant reply this in a java desktop class.
Related
I have created a simple websocket server with this configuration and I'm trying to subscribe or unsubscribe a user to a certain topic automaticaly, without the user permission (without sending SUBSCRIPTION event from the client). Example: a user connect to the server and the server automatically subscribe the user to topic "news" and "football". I'm trying to replicate what done when a client send this event "SUBSCRIBE\ndestination:/news/e88e2682-c467-4f48-b445-0af9c7983a5d\nid:/news/e88e2682-c467-4f48-b445-0af9c7983a5d\n\n\u0000" but without the client sending it actualy, is it possible?
package com.websocket.socket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
import java.util.Arrays;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketConfiguration.class);
public static final String APP = "/app";
private static final String[] topics = new String[]{
NEWS.BROKER_NAME,
FOOTBALL.BROKER_NAME
};
#Autowired
private TopicChannelInterceptor topicChannelInterceptor;
#Autowired
private UserSessions userSessions;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker(topics);
config.setApplicationDestinationPrefixes(APP);
LOGGER.info("Prefix: {}", APP);
LOGGER.info("Topics: {}", Arrays.toString(topics));
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket")
.setAllowedOriginPatterns("*")
.withSockJS();
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(topicChannelInterceptor);
WebSocketMessageBrokerConfigurer.super.configureClientInboundChannel(registration);
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
#Override
public WebSocketHandler decorate(final WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
#Override
public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
// We will store current user's session into WebsocketSessionHolder after connection is established
userSessions.addUserSession(session.getId(), session);
LOGGER.info("Session stored");
super.afterConnectionEstablished(session);
}
};
}
});
}
}
I know how to test websocket, like use WebSocketKing or Postman(2021/5/20), and it works good
I have no idea about writing websocket in unit test
usually, I use "SpringJUnit4ClassRunner" and "#WebAppConfiguration" to mock my service and test my controller
but how to create a mock websocket server to test my websocket in unit test?
Can I use the way like my SimpleControllerTest to create a mock webSocket server?
if I can , How to do it?
and sorry about my bad English , thanks all
Environmen
Java : 1.8
Server : Tomcat 8.5
Test : Junit 4
Socket : javax.websocket.jar
Framework : Spring MVC
My Simple WebSocket Code
package com.ws.socket;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#ServerEndpoint(value = "/MyEndpoint")
public class Socket {
private SocketConnection socketConnection;
#OnOpen
public void onOpen(Session session) {
socketConnection = new SocketConnectionImpl(session);
socketConnection.onOpen(session.getId());
}
#OnClose
public void onClose(Session session) {
socketConnection.onClose(session.getId());
}
#OnMessage
public void onMessage(Session session, String msg) throws IOException {
socketConnection.onMessage(session, msg);
}
#OnError
public void onError(Session session, Throwable error) {
socketConnection.onError(session.getId(), error);
}
}
public abstract class SocketConnection {
protected SocketConnection(Session session) {
}
protected void onOpen(String sessionId) {
System.out.println(sessionId + " build websocket connection !");
}
protected void onClose(String sessionId) {
System.out.println(sessionId + " close connection !");
}
protected void onMessage(Session session, String msg) throws IOException {
System.out.println(session.getId() + " say : " + msg);
session.getBasicRemote().sendText(" already receive msg about your say " + msg);
}
protected void onError(String sessionId, Throwable error) {
System.out.println(sessionId + " get error , message = " + error.getMessage());
}
}
My Simple Test Controller
package com.ws.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SimpleController {
#GetMapping("/test")
public String testService() {
return "start success!";
}
}
My Simple Controller Test
package com.ws.controller;
import com.ws.config.ServletConfig;
import com.ws.config.SpringConfig;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
#WebAppConfiguration
#ContextConfiguration(classes = {ServletConfig.class, SpringConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class SimpleControllerTest {
#Autowired
WebApplicationContext webApplicationContext;
MockMvc mvc;
#Before
public void init() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.build();
}
#Test
public void testAPI() throws Exception {
String result = mvc.perform(get("/test")).andReturn().getResponse().getContentAsString();
Assert.assertEquals("start success!", result);
}
}
I'm new to vert.x and decided to start out light, but can't get a simple unit test to work (using maven to build). Code as follows
FileRepo.java:
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Router;
public class FileRepo extends AbstractVerticle {
#Override
public void start(Future<Void> fut) {
HttpServer server = vertx.createHttpServer();
Router router = Router.router(vertx);
router.route("/upload").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.putHeader("content-type", "text/plain");
response.end("Hello world!");
});
System.out.println("Starting server!");
server.requestHandler(router::accept).listen(8080);
System.out.println("Server started!");
}
}
FileRepoTest.java:
import FileRepo;
import io.vertx.core.Vertx;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(VertxUnitRunner.class)
public class FileRepoTest {
private Vertx vertx;
#Before
public void setUp(TestContext context) {
vertx = Vertx.vertx();
System.out.println("SetUp Vertx");
vertx.deployVerticle(FileRepo.class.getName(), context.asyncAssertSuccess());
System.out.println("SetUp done");
}
#After
public void tearDown(TestContext context) {
System.out.println("tearDown Vertx");
vertx.close(context.asyncAssertSuccess());
}
#Test
public void testUpload(TestContext context) {
System.out.println("testUpload");
}
}
Result:
SetUp Vertx
SetUp done
Starting server!
Server started!
tearDown Vertx
java.util.concurrent.TimeoutException
at io.vertx.ext.unit.impl.TestContextImpl$Step.lambda$run$0(TestContextImpl.java:112)
at java.lang.Thread.run(Thread.java:745)
Process finished with exit code -1
Browsing http://localhost:8080/upload while waiting for the TimeoutException shows a Hello World! page, but the #Test never seem to execute. What am I doing wrong here?
Regards,
Mattias
The exception you are getting is because there is no acknowledgment of the server start state.
Change your Verticle to following:
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Router;
public class FileRepo extends AbstractVerticle {
#Override
public void start(Future<Void> fut) {
Router router = Router.router(vertx);
router.route("/upload").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.putHeader("content-type", "text/plain");
response.end("Hello world!");
});
System.out.println("Starting server!");
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(8080, result -> {
if (result.succeeded()) {
System.out.println("Server started!");
fut.complete();
} else {
System.out.println("Server start failed!");
fut.fail(result.cause());
}
});
}
}
As per the documentation of activemq we need to set the http://activemq.apache.org/stomp client-id header to have durable subscriptions.
I set the client-id in connect headers and activemq.subscriptionName in subscription headers as shown below, however I am not seeing the desired behavior. Do we need to set anything on the web socket configuration and message side too?
Here is the subscription code
var headers = {
// additional header
'client-id': 'my-client-id'
};
var subscription_headers = {
// additional header
'activemq.subscriptionName': 'my-client-id'
};
var connect = function () {
var socket = new SockJS( webSocketUrl );
stompClient = Stomp.over( socket );
stompClient.connect( headers, function ( frame ) {
console.log( 'Connected: ' + frame );
stompClient.subscribe( topic, function ( message ) {
.....
.....
}, subscription_headers);
}, function(frame) {
console.log("Web socket disconnected");
});
}
Websocket configuration
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
#Value("${spring.websocket.activemq.relay.host}")
private String relayHost;
#Autowired
#Value("${spring.websocket.activemq.relay.port}")
private int relayPort;
#Autowired
#Value("${spring.activemq.user}")
private String activeMqLogin;
#Autowired
#Value("${spring.activemq.password}")
private String activeMqPassword;
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/queue/", "/topic/")
.setRelayHost(relayHost)
.setRelayPort(relayPort)
.setSystemLogin(activeMqLogin)
.setSystemPasscode(activeMqPassword);
registry.setApplicationDestinationPrefixes("/testbrkr");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/exWs").withSockJS();
}
}
This worked, passing the headers directly in the function as shown
var connect = function () {
var socket = new SockJS( webSocketUrl );
stompClient = Stomp.over( socket );
stompClient.connect( {"client-id": "my-client-id"},, function ( frame ) {
console.log( 'Connected: ' + frame );
stompClient.subscribe( topic, function ( message ) {
.....
.....
}, {"activemq.subscriptionName": "my-client-id"});
}, function(frame) {
console.log("Web socket disconnected");
});
}
Hi i have an issue with spring websockets, this is the scenario:
a standalone application is sending (remote) some data like date Date, procedence String, and weight BigDecimal this data is sending via TCP to socket,
after that this data is saving into database at this point all is fine, but in the next step (websocket) i cannot show this information in a webpage, the weight data must be showed (live) in the screen
this is my websocket configuration:
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
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;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/indicator").withSockJS();
}
#Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
}
#Override
public void configureClientOutboundChannel(final ChannelRegistration registration) {
}
#Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration wstr) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> list) {
return Boolean.TRUE;
}
}
this is my another class that receive data form socket and process information and send to websocket:
import com.mcss.mcontrols.helper.ByteHelper;
import com.spc.basweb.Constants;
import com.spc.basweb.transmissor.dto.Transmission;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.simp.broker.BrokerAvailabilityEvent;
import com.spc.basweb.service.BroadcastingService;
import com.spc.basweb.service.DataProcessorService;
import java.io.IOException;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.annotation.Transformer;
#MessageEndpoint
public class BroadcastingServiceImpl implements BroadcastingService, ApplicationListener<BrokerAvailabilityEvent> {
private static final Logger LOGGER = Logger.getLogger(BroadcastingServiceImpl.class);
private final MessageSendingOperations<String> messagingTemplate;
private String processedData;
#Autowired
DataProcessorService dataProcessorService;
#Autowired
public BroadcastingServiceImpl(final MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#Override
public String getProcessedData() {
return processedData;
}
#Override
#ServiceActivator(inputChannel = "broadcaster")
public String broadcast(byte[] bytes) {
try {
Transmission t = (Transmission) ByteHelper.toObject(bytes);
LOGGER.debug(t.getProcedence() + " " + t.getDate() + " " + t.getWeight());
String rm = this.dataProcessorService.processData(t);
this.messagingTemplate.convertAndSend(Constants.END_POINT_READ, this.dataProcessorService.getWeighing().getWeight().toString());
return rm;
} catch (IOException | ClassNotFoundException ex) {
LOGGER.error("Error de transmision de objetos", ex);
}
return DataProcessorService.NOT_OK_RESPONSE;
}
#Override
public void onApplicationEvent(BrokerAvailabilityEvent e) {
LOGGER.debug("Application event");
}
#Transformer(outputChannel = "broadcaster")
public String convert(String response) {
return response;
}
}
in the debbuger i'm getting this information:
30-03-2016 15:07:20 DEBUG SimpleBrokerMessageHandler:277 - Processing MESSAGE destination=/read session=null payload=3003
in another class (Controller) i'm using the same method:
this.messagingTemplate.convertAndSend(Constants.END_POINT_READ, "3500");
and sending "manually" the information an is showing correctly. and i'm getting in debbuger this message:
30-03-2016 15:05:18 DEBUG SimpleBrokerMessageHandler:277 - Processing MESSAGE destination=/read session=dfR45V77 payload=3500
the difference is in session value but i don't know what this session is having null in the process, what am i doing wrong some clarification o help is welcome
First of all I don't see the configureMessageBroker implementation, so it isn't clear how that may work at all...
From other hand if you see such a difference, try to debug the code in the SimpMessagingTemplate.
I only see headerAccessor.setSessionId(sessionId); in the SimpleBrokerMessageHandler when it does
logger.debug("Broadcasting to " + subscriptions.size() + " sessions.");