Web socket java client for Stomp-Spring server side.

Problem :

As an analysis for an issue I had to write a simple java web socket client site code connecting to a Spring based application on the server side with a stomp based web socket broker .

Solution :

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rnd</groupId>
    <artifactId>SockWebsocketClient</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>9.0.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.0.2.RELEASE</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.0.7.RELEASE</version>
        </dependency>
    </dependencies>
</project>

Sock JS Client

Its the main class , a simple one doing the basic job . As most of the web sockets these days are meant to use SSL , so tried to by pass the trust manager issue by creating a dummy TrustManager .

The input to this code is the topic string you want to subscribe to .

Also dont miss to change the <URL> string in the code before you run , pointing to your web socket url.

package com.client;

import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SimpleMessageConverter;
import org.springframework.messaging.converter.StringMessageConverter;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SockJsClient {


    public static void main(String[] args) {

        try {
            StandardWebSocketClient simpleWebSocketClient = new StandardWebSocketClient();


            TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                }
                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                }
            }
            };


            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

            Map<String, Object> properties = new HashMap<>();
            properties.put("org.apache.tomcat.websocket.SSL_CONTEXT", sc);
            simpleWebSocketClient.setUserProperties(properties);

            List<Transport> transports = new ArrayList();
            transports.add(new WebSocketTransport(simpleWebSocketClient));

            org.springframework.web.socket.sockjs.client.SockJsClient sockJsClient = new org.springframework.web.socket.sockjs.client.SockJsClient(transports);
            sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
            WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
            stompClient.setMessageConverter(new MessageConverter() {
                @Override
                public Object fromMessage(Message<?> message, Class<?> aClass) {
                    return new String((byte[])message.getPayload());
                }

                @Override
                public Message<?> toMessage(Object o, MessageHeaders messageHeaders) {
                    return null;
                }
            });

            // url : pointing to websocket as sockJs first tries to get info by sending a HTTP request
            // and then sends an upgrade request to ws or wss. So your url should be some thing like htttp://

            URI stompUrlEndpoint = new URI("<url>");

            WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders();
            StompHeaders connectHeaders = new StompHeaders();

            /*Can set connection header like login , passcode in connectHeaders  */

            stompClient.connect(stompUrlEndpoint.toString(), handshakeHeaders, connectHeaders, new SessionHandler(args[0]), new Object[0]);

            Thread.sleep(30000);
            stompClient.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Session Handler

Every socket connection need a session handler and here is one which takes care of connection setup and in-coming messages.

package com.client;

import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;

import java.lang.reflect.Type;

public class SessionHandler extends StompSessionHandlerAdapter {

    private final String topicName ;

    public SessionHandler(String topicName) {
        this.topicName = topicName;
    }

    @Override
    public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
        this.subscribeTo(session);
    }

    @Override
    public void handleException(StompSession session,StompCommand command,StompHeaders headers,byte[] payload,Throwable exception ) {
        exception.printStackTrace();
    }


    public void handleFrame(StompHeaders headers, Object payload) {
        System.out.println(payload.toString());
    }


    @Override
    public void handleTransportError(StompSession session, Throwable exception) {
        exception.printStackTrace();
    }

    private void subscribeTo(StompSession session) {
        StompHeaders headers = new StompHeaders();
        headers.add("id","websocket-session-standalone-0");
        headers.add("destination",topicName);

        session.subscribe(headers, new StompFrameHandler() {
            @Override
            public Type getPayloadType(StompHeaders stompHeaders) {
                return String.class;
            }

            @Override
            public void handleFrame(StompHeaders stompHeaders, Object o) {
                System.out.println( " Message is " +
                        o.toString()
                );
            }
        });
    }
}

Thats it !! .

Comments are closed.

Blog at WordPress.com.

Up ↑

%d bloggers like this: