I'm trying to implement web sockets to listen to a server for messages. I've made a sample project which works successfully and I'm now trying to port it over to our existing code base. When I run the application I get the following error:
2019-08-29 09:34:33 - org.springframework.web.socket.sockjs.client.SockJsClient: [AWT-EventQueue-0] ERROR - Initial SockJS "Info" request to server failed, url=ws://localhost:8080/igip-gateway/messaging
java.lang.NoSuchMethodError: org.springframework.http.ResponseEntity.status(I)Lorg/springframework/http/ResponseEntity$BodyBuilder;
at org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport.lambda$static$1(RestTemplateXhrTransport.java:158) ~[spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:496) ~[org.springframework.web-3.2.3.RELEASE.jar:3.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:465) ~[org.springframework.web-3.2.3.RELEASE.jar:3.2.3.RELEASE]
at org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport.executeInfoRequestInternal(RestTemplateXhrTransport.java:137) ~[spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.socket.sockjs.client.AbstractXhrTransport.executeInfoRequest(AbstractXhrTransport.java:129) ~[spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.socket.sockjs.client.SockJsClient.getServerInfo(SockJsClient.java:294) ~[spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.socket.sockjs.client.SockJsClient.doHandshake(SockJsClient.java:260) ~[spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.socket.messaging.WebSocketStompClient.connect(WebSocketStompClient.java:268) [spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.web.socket.messaging.WebSocketStompClient.connect(WebSocketStompClient.java:249) [spring-websocket-5.1.9.RELEASE.jar:5.1.9.RELEASE]
I can see the method exists as a class in an external library as part of spring-web-5.1.9.RELEASE.jar and when I run the application with verbose:class the only references I can see to that jar are:
[Loaded org.springframework.http.converter.json.Jackson2ObjectMapperBuilder from file:/C:/DEV/m2/org/springframework/spring-web/5.1.9.RELEASE/spring-web-5.1.9.RELEASE.jar]
[Loaded org.springframework.http.converter.json.SpringHandlerInstantiator from file:/C:/DEV/m2/org/springframework/spring-web/5.1.9.RELEASE/spring-web-5.1.9.RELEASE.jar]
[Loaded org.springframework.http.HttpLogging from file:/C:/DEV/m2/org/springframework/spring-web/5.1.9.RELEASE/spring-web-5.1.9.RELEASE.jar]
Here is my code as well in case it's relevant:
private static String URL = "ws://localhost:8080/gateway/messaging";
public static void start() {
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(webSocketClient));
WebSocketClient client = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(client);
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setThreadNamePrefix("Heart-Beat-Executor-");
threadPoolTaskScheduler.setPoolSize(1);
threadPoolTaskScheduler.initialize();
stompClient.setTaskScheduler(threadPoolTaskScheduler);
stompClient.setMessageConverter(new StringMessageConverter());
StompHeaders connectHeaders = new StompHeaders();
connectHeaders.setHeartbeat(new long[]{20000, 20000});
connectHeaders.add("SECURITY-TOKEN", "token");
StompSessionHandler sessionHandler = new CallListeningStompSessionHandler();
ListenableFuture<StompSession> connect = stompClient.connect(URL, new WebSocketHttpHeaders(), connectHeaders, sessionHandler);
System.out.println(connect);
}
public class CallListeningStompSessionHandler extends StompSessionHandlerAdapter {
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
StompHeaders connectHeaders = new StompHeaders();
connectHeaders.add("SECURITY-TOKEN", "token");
connectHeaders.add("destination", "/topic/services/user");
session.subscribe(connectHeaders, this);
}
#Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
}
#Override
public Type getPayloadType(StompHeaders headers) {
return String.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
System.out.println(payload);
}
}
What is the reason I am getting this error?
Related
I'm testing a Ocpp Server comunication using Spring Websocket. The handshake works well, I can interact with the client when a station send a message (BootNotification,StatusNotification...). But sometimes I need to send things with the server (request remote transaction, get informations, etc), without the station send first.
How can I access a open session (example: ws:localhost:8080/central/station01) with another service?
My Wesocket config:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/central/**")
.addInterceptors(new HttpSessionHandshakeInterceptor());
}
}
My WebSocket Handler:
public class MyHandler extends TextWebSocketHandler implements SubProtocolCapable {
private final String[] subProtocols = {"ocpp1.6", "ocpp2.0"};
#Autowired
private ClientRepository clientRepo;
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
Global.id="";
Global.session="";
Global.client="Close";
System.out.print("\n Connection Close \n"+"Session: "+session.getId()+"\n");
session.getHandshakeHeaders();
System.out.print("session enabled"+session);
}
#Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception{
Global.id=session.getId();
Global.session=session.getUri().toString();
Global.client="ok";
Client aa= new Client(null,session.getId(),session.getUri().toString(),"ok","");
System.out.print("\n Connected \n"+"Session: "+session.getId()+"\n");
System.out.print(session.getUri());
}
#Override
public void handleMessage(WebSocketSession session,WebSocketMessage<?> message) throws Exception {
//WebSocketHttpHeaders expectedHeaders = new WebSocketHttpHeaders();
System.out.print("\n "+message.getPayload());
Integer id=0;
Global.ocpp=Global.ocpp+" \n "+message.getPayload().toString();
ZonedDateTime data = ZonedDateTime.now();
BootNotificationResponse stat=new BootNotificationResponse("Accepted",data.toString().substring(0,data.toString().length()-"[America/Sao_Paulo]".length()),300);
JSONArray mm=new JSONArray((message.getPayload()).toString());
id=(int )mm.get(0)+1;
// session.sendMessage(new TextMessage(message.getPayload().toString()));
// System.out.print("\n Remote: "+session.getRemoteAddress()+"\n");
JSONObject ss=new JSONObject(stat);
System.out.print(session.getHandshakeHeaders());
JSONArray ja = new JSONArray();
ja.put(3);
ja.put(mm.get(1));
//
ja.put(ss);
// System.out.print("\n"+message.getPayload()+"\n");
// System.out.print(mm.get(2)+"\n");
Client dados=new Client(null,Global.id,Global.session,Global.client,message.getPayload().toString());
clientRepo.save(dados);
if(mm.get(2).equals("Authorize")) {
JSONArray nob = new JSONArray();
JSONObject iii=new JSONObject(new Auth(new AuthorizeResponse("1233434","ddfd","Accepted")));
nob.put(3);
nob.put(mm.get(1));
nob.put(iii);
System.out.print(nob);
//[2,"4","Authorize",{"idToken":{"idToken":"111111","type":"ISO14443"},"evseId":[1]}]
session.sendMessage(new TextMessage(nob.toString()));
}
if(mm.get(2).equals("BootNotification")) {
System.out.print("Boot \n");
session.sendMessage(new TextMessage(ja.toString()));
}
}
#Override
public List<String> getSubProtocols() {
System.out.print(Arrays.asList(subProtocols));
return Arrays.asList(subProtocols);
}
}
You need to do plenty of things to send command from your side to station.
When station connect to your server, you need to keep that session
in a Map with chargepointId probably.
If you would like to send
command then use that session then use websocket instance to send
command to station.
To initiate Step-2, you need to have service or
API to initiate it.
Should you need information, follow this URL: https://github.com/ChargeTimeEU/Java-OCA-OCPP
I am new to both springboot and websockets so please be gentle and this is my last question chance on this account. I have a websocket jar that sends messages from two urls ws:localhost/operations and ws:localhost/prices from 8080 port. My task is to read those messages. The Jar file streams messages like:
For operation:
"data":{ "description":"lorem ipsum", "id":"OJ1136453723" },
"type":"DELETE"
For price:
"data":{ "price":1384.1685, "id":"WN6427148286" }, "type":"PRICE"
I have spring-boot-starter-websocket, spring-boot-starter-security and lombok as dependencies.
I seem to be connecting to the jar file, because powershell says "Socket connected
Frame TEXT (fin=true, buffer len = 48)" when I run my code but it only hits the Application breakpoints whenever I debug, it doesnt even go to controller or config classes. It may be an obvious mistake but I am a bit lost so any help would be appriciated. Also, my port for this client is 8443 which is specified in application.yml and it starts there.
My Client:
public class WSClient{
private static String URL = "ws://localhost:8080";
public static void main(String[] args) throws Exception {
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(URL, sessionHandler);
new Scanner(System.in).nextLine();
}
Controller:
#Controller
public class MsgController {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/operations")
public Operations operations(Operations operations)throws Exception{
System.out.println(operations);
simpMessagingTemplate.convertAndSend("/operations", operations);
return operations;
}
StompSessionHandler:
public class MyStompSessionHandler extends StompSessionHandlerAdapter {
private Logger logger = LogManager.getLogger(MyStompSessionHandler.class);
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
logger.info("New session established : " + session.getSessionId());
session.subscribe("/operations", this);
System.out.println("Subscribed to /operations");
logger.info("Subscribed to /operations");
}
#Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
logger.error("Got an exception", exception);
}
#Override
public Type getPayloadType(StompHeaders headers) {
return Operations.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
Operations ops = (Operations) payload;
System.out.println(ops);
}
}
Operations:
#lombok.Data
public class Operations{
public String description;
public String id;
public String type;
}
EDIT:
Thanks to help of #OkanKonur the problem is that StompHandler's afterConnected is not called even though it seems like they are connected. We tried it with the github link he's given in the comments and it seems to work. The jar file is most probably not a spring project so is there a way to solve this? It still can't read anything.
I try to receive a protobuf message out of RabbitMQ using Spring Integration.
My integration flow:
public class FacadeIntegrationFlowAdapter extends IntegrationFlowAdapter {
#SuppressWarnings("rawtypes")
private final Facade facade;
private final FacadeProperties facadeProperties;
#SuppressWarnings("unchecked")
#Override
protected IntegrationFlowDefinition<?> buildFlow() {
return from(facadeProperties.getQueueName())
.handle(facade::getNewMessages);
}
}
The getNewMessages method:
#Override
public ExchangeResponse getNewMessages(Message<ExchangeRequest> message) {
ExchangeRequest request = message.getPayload();
log.info("Receiving new message: " + request.toString());
This is how I send the message to the queue. It's so simple to make the test easy to follow.
ExchangeRequest request = ExchangeRequest.newBuilder()
.addAllAuthors(List.of("author1", "author2"))
.addAllBooks(List.of("book1", "book2"))
.build();
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setUsername("user");
connectionFactory.setPassword("password");
connectionFactory.setHost("localhost");
connectionFactory.setPort(24130);
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
var basicProperties = new AMQP.BasicProperties().builder()
.contentType("application/x-protobuf")
.type(request.getDescriptorForType().getFullName())
.build();
channel.basicPublish(
"facade-exchange", "facade-routing-key", basicProperties, request.toByteArray());
} catch (IOException e) {
Unfortunately, I keep getting the exception:
com.google.protobuf.InvalidProtocolBufferException: Type of the Any message does not match the given class.
However, when I change the getNewMessages method to the following, all seems fine.
#Override
public ExchangeResponse getNewMessages(Message message) {
try {
Any payload = (Any) message.getPayload();
ByteString value = payload.getValue();
ExchangeRequest request = ExchangeRequest.parseFrom(value);
log.info("Receiving new message: " + request.toString());
Where do I make a mistake? Tx!
So i'm trying to send an object from client->server & server->client with WebSocket. Sending object from client->server works fine, meanwhile server->client throw an exception
org.springframework.messaging.converter.MessageConversionException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
Here is the class i'm trying to send
#Data
#AllArgsConstructor
#NoArgsConstructor
public class TextMessage {
private String sender;
private String room;
private String message;
}
and this is the code on the client-side
public class TelepatiClient {
public static void main(String[] args) {
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
stompClient.setTaskScheduler(new ConcurrentTaskScheduler());
String url = "ws://localhost:8000/connect";
StompSessionHandler handler = new TelepatiSessionHandler();
stompClient.connect(url, handler);
new Scanner(System.in).nextLine();
}
}
public class TelepatiSessionHandler extends StompSessionHandlerAdapter {
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/room/global", this);
session.send("/test", new TextMessage("test", "test", "test"));
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
System.out.println(payload.toString());
}
#Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
exception.printStackTrace();
super.handleException(session, command, headers, payload, exception);
}
}
and this is message controller on the server-side
#Controller
public class TelepatiController {
#MessageMapping("/test")
#SendTo("/room/global")
public TextMessage getMessage(TextMessage message) {
System.out.println("get message :" + message.toString());
return new TextMessage("test2", "test2", "test2");
}
}
i was able to run System.out.println("get message :" + message.toString());, but get message convertion exception on the client-side when returning new TextMessage("test2", "test2", "test2");. From my test before, returning a String object works fine, why returning TextMessage object not working? How can i send any object (in this case TextMessage) from server->client? Thanks!
Well the problem is the content. In this line:
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
You indicate that the client uses a json converter. So, your client is always expecting a JSON object.
But in your test, in this line:
return new TextMessage("test2", "test2", "test2");
You are sending plain text. Due the StompClient is thrown an exception
org.springframework.messaging.converter.MessageConversionException
Because the message in text plain is not JSON object.
I hope someone helps you, I had the same problem, what I did was tell the topler handler, to which I subscribe, the type of payload that will return.
This is the handler of my stompClient:
public class TelepatiSessionHandler extends StompSessionHandlerAdapter {
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
session.send("/test", new TextMessage("test", "test", "test"));
}
#Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
exception.printStackTrace();
super.handleException(session, command, headers, payload, exception);
}
}
And this is the handler for the topic to which I subscribe
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
stompClient.setTaskScheduler(new ConcurrentTaskScheduler());
String url = "ws://localhost:8000/connect";
StompSessionHandler handler = new TelepatiSessionHandler();
StompSession session = stompClient.connect(url, handler).get();
session.subscribe("/room/global", new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers) {
return TextMessage.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
TextMessage textMessage = (TextMessage) payload;
System.out.println(textMessage.toString());
}
});
Here is a complete example:
https://github.com/jaysridhar/spring-websocket-client/blob/master/src/main/java/sample/Application.java
I have this code for server:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(Handlers.path()
.addPrefixPath("/item", new ItemHandler())
)
.build();
server.start();
And handler:
private class ItemHandler implements HttpHandler {
#Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
exchange.getPathParameters(); // always null
//ItemModel item = new ItemModel(1);
//exchange.getResponseSender().send(mapper.writeValueAsString(item));
}
}
I want to send request /item/10 and get 10 in my parameter. How to specify path and get it?
You need a PathTemplateHandler and not a PathHandler, see:
Undertow server = Undertow.builder()
.addHttpListener(8080, "0.0.0.0")
.setHandler(Handlers.pathTemplate()
.add("/item/{itemId}", new ItemHandler())
)
.build();
server.start();
Then, in your ItemHandler:
class ItemHandler implements HttpHandler {
#Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json");
// Method 1
PathTemplateMatch pathMatch = exchange.getAttachment(PathTemplateMatch.ATTACHMENT_KEY);
String itemId1 = pathMatch.getParameters().get("itemId");
// Method 2
String itemId2 = exchange.getQueryParameters().get("itemId").getFirst();
}
}
The method 2 works because Undertow merges parameters in the path with the query parameters by default.
If you do not want this behavior, you can use instead:
Handlers.pathTemplate(false)
The same applies to the RoutingHandler, this is probably what you want to use eventually to handle multiple paths.
Handlers.rounting() or Handlers.routing(false)