function ReconnectingStompClient(url, options) {

    options = Object.assign({
        onConnect: function () {
        },
        onDisconnect: function () {
        },
        subscriptionListeners: []
    }, options || {});

    var state = {
        connected: false,
        closed: false,
        subscriptionListeners: options.subscriptionListeners,
        stompClient: null
    };

    var doSubscribe = function (listener) {
        listener.subscription = state.stompClient.subscribe(
            listener.destination,
            (message) => {
                try {
                    message.data = JSON.parse(message.body);
                } catch (ignored) {
                }
                listener.onMessage(message)
            },
            listener.headers
        );
    }

    var doConnect = function (attemptIndex = 0) {

        console.log("STOMP connection attempt - " + attemptIndex);

        state.stompClient = webstomp.over(new SockJS(url));

        state.stompClient
            .connect(
                {},
                (frame) => {
                    state.connected = true;
                    state.subscriptionListeners.forEach(listener => doSubscribe(listener));
                    options.onConnect(frame);
                },
                (event) => {
                    state.connected = false;
                    options.onDisconnect(event);
                    if (!state.closed) { // do the reconnect
                        setTimeout(() => doConnect(attemptIndex + 1), Math.min(15000, Math.round(Math.pow(2, attemptIndex) * 250))) // try to reconnect each 15 seconds at most
                    }

                }
            );

    }

    doConnect();

    this.connected = function () {

        return state.connected;

    }

    this.disconnect = function () {

        state.closed = true;

        state.stompClient.disconnect();

    }

    this.subscribe = function (destination, onMessage, headers = {}) {

        var listener = {destination: destination, onMessage: onMessage, headers: headers, subscription: null};

        state.subscriptionListeners.push(listener);

        if (state.connected) {

            doSubscribe(listener);

        }

    }

    this.unsubscribe = function (destination) {

        var remainingListeners = [];

        state
            .subscriptionListeners
            .forEach(listener => {
                if (listener.destination === destination) {
                    try {
                        listener.subscription.unsubscribe();
                    } catch (e) {
                        console.warn("Error during unsubscribe: " + e);
                    }
                } else {
                    remainingListeners.push(listener);
                }
            });

        state.subscriptionListeners = remainingListeners;

    }

    this.send = function (destination, message, headers = {}) {

        return state.stompClient.send(
            destination,
            message,
            headers
        );

    }

    this.sendJson = function (destination, message, headers) {

        return this.send(destination, JSON.stringify(message), headers);

    };

}