Skip to content
This repository has been archived by the owner on Jan 6, 2023. It is now read-only.

how to stop auto web-socket connection and do it manually #121

Closed
zymr-keshav opened this issue Dec 7, 2018 · 28 comments
Closed

how to stop auto web-socket connection and do it manually #121

zymr-keshav opened this issue Dec 7, 2018 · 28 comments

Comments

@zymr-keshav
Copy link

In your example, WebSocket connection is done automatically. I want to call before connect and other things also mentioned the same that "In this sample, STOMP broker is connected during the Angular DI class initialization phase"

How to call the connection manually in one of my components as soon as that component getting loaded in the page.

can we have sample example?

@kum-deepak
Copy link
Member

I will write an example soon. Meanwhile please see stomp-js/rx-stomp#495

@zymr-keshav
Copy link
Author

zymr-keshav commented Dec 8, 2018

Thank you, Deepak. one more thing I want to know. this implementation works fine but whenever page reloaded, WebSocket connection established again so I lost the previous data of the previous topic/channel. How can I persist the connection?

I wanted to utilize beforeConnect or onConnect method but how do I use those? they are not the part of rxStompservice?
I have import these many in my component

mport { Message, Frame, Client } from '@stomp/stompjs';
import { RxStompService, InjectableRxStompConfig } from '@stomp/ng2-stompjs';
import { RxStompState } from '@stomp/rx-stomp';

I know the above method are part of Client import but how to use? I tried this way

client = new Client();

then later

    this.client.onConnect = (cb: Frame) => {
            console.log('called on Connect', cb);
        };

but it never print anything in console? why so

@kum-deepak
Copy link
Member

RxStompService extends RxStomp and InjectableRxStompConfig extends RxStompConfig.

So, simplly add beforeConnect to your config. Please check https://stomp-js.github.io/api-docs/latest/classes/RxStompConfig.html#beforeConnect.

Please note that https://stomp-js.github.io/api-docs/latest/classes/RxStomp.html#configure can be called multiple times.

@zymr-keshav
Copy link
Author

zymr-keshav commented Dec 10, 2018

But there is no such method as onConnect in RxStompConfig but available in Client

Also, I have tried to call beforeConnect() but it never being called as I notice that on page reload/refresh every WebSocket connection getting reset and reconnect but this method is never being called. what I have tried is below ( relevant code only)

import { RxStompService, InjectableRxStompConfig } from '@stomp/ng2-stompjs';
export class NotificationComponent implements OnInit, OnDestroy, AfterContentInit {
 constructor(
        private rxStompService: RxStompService,
        private rxStompConfig: InjectableRxStompConfig
    ) {}

   ngAfterContentInit() {
        this.initStomp();
    }

    private initStomp() {
        this.checkConnection();
        const stompConfig: InjectableRxStompConfig = Object.assign({}, myRxStompConfig, {
            connectHeaders: {
                access_token: sessionStorage.getItem('token')
            }
        });

        this.rxStompService.configure(stompConfig);
        this.rxStompService.activate();
       // other things to do 
}

 private checkConnection() {
     this.rxStompConfig.beforeConnect = () => {
            console.log('%c called before connect', 'color: blue');
        };
}

}

@kum-deepak
Copy link
Member

Please change your code as follows and try:

import { RxStompService, InjectableRxStompConfig } from '@stomp/ng2-stompjs';
export class NotificationComponent implements OnInit, OnDestroy, AfterContentInit {
    constructor(
        private rxStompService: RxStompService,
        private rxStompConfig: InjectableRxStompConfig
    ) {}

    ngAfterContentInit() {
        this.initStomp();
    }

    private initStomp() {
        // Replace if still needed
        // this.checkConnection();
        const stompConfig: InjectableRxStompConfig = Object.assign({}, myRxStompConfig, {
            connectHeaders: {
                access_token: sessionStorage.getItem('token')
            },
            beforeConnect: () => {
                console.log('%c called before connect', 'color: blue');
            }
        });

        this.rxStompService.configure(stompConfig);
        this.rxStompService.activate();
        // other things to do 
    }
}

The underlying onConnect from Client is exposed as an Observable RxStomp#connected$. Subscribe to this toexecute code after connection is established:

    this.rxStompService.connected$.subscribe(() => {
        // your code
    });

@zymr-keshav
Copy link
Author

zymr-keshav commented Dec 10, 2018

Thanks for quick reply, I figure it out how to work and did the same as you mentioned.
now I have done this.

checkConnection() {
        this.rxStompService.connected$.subscribe((state: RxStompState) => {
            console.log({ state });
            this.connectionStatus = RxStompState[state];
        });
    }

but the actual need of this is I want to set the maximum retry of WebSocket or to prevent the reconnection if already connected. How do I archive this?
I have tried with ReplaySubject(1) but no hope.

@kum-deepak
Copy link
Member

You may use https://stomp-js.github.io/api-docs/latest/classes/RxStomp.html#connected to check if it is connected.

@zymr-keshav
Copy link
Author

zymr-keshav commented Dec 10, 2018

I know this method but how does this help when page reload/refresh? Will you suggest an approach with stackblitz

@kum-deepak
Copy link
Member

It will return true if broker is connected.

@ibhargav90
Copy link

@kum-deepak Any update on this documentation.?

@kum-deepak
Copy link
Member

@ibhargav90 what specifically are you looking for?

@ibhargav90
Copy link

Looking for this,

I know this method but how does this help when page reload/refresh? Will you suggest an approach with stackblitz

On page reload, how can i persist connection.

@kum-deepak
Copy link
Member

It is not possible to persist the connection on a page reload.

@ibhargav90
Copy link

ibhargav90 commented Apr 25, 2019

        const stompConfig: InjectableRxStompConfig = {
            ...RxStompConfig,
            connectHeaders: this.headers,
            beforeConnect: () => {
              this.rxStompService.connectionState$
		.pipe(filter((state: number) => state === StompState.CLOSED))
		.subscribe(() => {
			console.log('Retrieval count after subscribe', this.numRetries);
			if (this.numRetries <= 0) {
				this.rxStompService.deactivate();
			}
			this.numRetries--;
		});
            }
        };
        this.rxStompService.connected$.pipe(skip(1)).subscribe(() => {
            this.numRetries = this.MAX_RETRIES; // reset the number of tries while connected back
            this.sendMessage(this.command.INCIDENT_LIST);
            this.topicSubscription = this.rxStompService
                .watch('/topic/${this.uniqueID}', {
                    ...this.headers,
                    id: 'topic_1'
                })
                .pipe(takeUntil(this.ngUnsubscribe))
                .subscribe((message: Message) => {
                    const receivedMessage = JSON.parse(message.body);
                    console.table({ receivedMessage });
                    const isConnected = this.rxStompService.connected();
                    this.service.setConnectionStatus(isConnected);
                    // do task for watching 
                });
        });

        this.rxStompService.configure(stompConfig);
        this.rxStompService.activate();
        this.isConnectionEstablished = true;

@kum-deepak
I have done this as per this discussion, however i can still get multiple subscription for topic.
Do you suggest anything that can be helpful for unsubscribing connectionState$ and topicSubscription$ ?

The connection is closed after sometime automatically, i want to unsubscribe all the topics and connectedState so we can avoid multiple subscription?

@kum-deepak
Copy link
Member

Your code has issues and is likely to lead to issues like multiple subscriptions and leaks. Unless your requirements are very specific, please follow the tutorial (https://stomp-js.github.io/guide/ng2-stompjs/2018/11/04/ng2-stomp-with-angular7.html) and sample (https://github.com/stomp-js/ng2-stompjs-angular7). These are based on our production applications.

The library itself does not disconnect on its own, there is likely to be some other reason (please keep that discussion in #146).

@mauicoder
Copy link

Hi Deepak,
I'm trying to disable the auto-connection (it is performed from angular-DI). As written in the title, is there a way to disable the auto-connect ? It would be nice to have a disable auto-connect in the configuration.
I tried to use the beforeConnect in my rx-stomp-config.ts, it is called before connecting, but the configuration is in a separated file where I have no access to the RXStompService instance, so I can't call deactivate().

I tried to use the AfterContentInit at app.component level, but the connection is already open.

What I need to achieve: do not connect if the client is not completely configured
So based on an internal check, do not even try to open the websocket.
How can I achieve that?
Thanks for your support!

@kum-deepak
Copy link
Member

@mauicoder I am making certain assumptions - you are on latest version, you are using something like following in the providers:

  providers: [
    {
      provide: InjectableRxStompConfig,
      useValue: myRxStompConfig
    },
    {
      provide: RxStompService,
      useFactory: rxStompServiceFactory,
      deps: [InjectableRxStompConfig]
    }
  ]

Change the above to:

  providers: [
    RxStompService
  ]

With this change, the created instance will not attempt to connect. When you have all required information, call the following to attempt connection:

    stompService.configure(config);
    stompService.activate();

Basically in this mode, the client remains inactive until .activate() is called.

@mauicoder
Copy link

Thanks for your answer Deepak
Your assumpions are correct and your solution looks great, I'll try it asap.

@cainaj
Copy link

cainaj commented Mar 27, 2020

Hi, I have a question about error handling with this library. I have something like:

startConnetionProcess() {
this.connecting = true;
this.pending = true;
this.authService.getIdToken()
.pipe(takeUntil(componentDestroyed(this)))
.subscribe(token => {
this.token = token;

  const stompConfig = {
    brokerURL: environment.webSocketUrl + '/api/listen',
    connectHeaders: {
      'token': this.token,
      topic: this.topic,
      env: this.env
    },
    debug(str) {
      console.log('STOMP: ' + str);
    }
  };
  this.connect(stompConfig);
});

}

where connect() calls a method that executes configure() and activate().

My problem comes when there is an error on the server side. If this connection is not established, I can see it if I have the debug option on my stompConfig config; however, inside of debug, i cannot call any functions apparently. I am using Angular 8. How can I handle an error? I haven't seen any references to error handling.

@kum-deepak
Copy link
Member

@cainaj moved to #189.

@DhanasekaranSelvaraj
Copy link

DhanasekaranSelvaraj commented Nov 19, 2020

Hi,

Is there any example to provide stompConfiguration using useFactory method since we will fetch domain URL on the fly and it is using sockJs protocal. Kindly find my below implementation based on your docs. I have removed brokerUrl field from myRXStomConfig.

Angular version: 9.1.3

Error: SyntaxError: Failed to construct 'WebSocket': The URL 'undefined' is invalid.

import { Client } from '@stomp/stompjs';
import * as SockJS from 'sockjs-client'

const client = new Client();
...

{
      provide: InjectableRxStompConfig,
      useValue: myRxStompConfig,
      multi: true
    },
    {
      provide: InjectableRxStompConfig,
      useFactory: client.webSocketFactory = () => new SockJS("Some https URL"),
      multi: true
    }

@kum-deepak
Copy link
Member

Please refer to the guide https://stomp-js.github.io/guide/ng2-stompjs/ng2-stomp-with-angular7.html and the sample at https://github.com/stomp-js/ng2-stompjs-angular7. I have recently tested - these still work for latest Angular and latest version of this library.

Currently you seem to be using @stomp/stompjs, with Angular you may be better off using @stomp/ng2-stompjs.

To your main question - please see #194 (comment).

@DhanasekaranSelvaraj
Copy link

Hi Deepak,

Thanks for your quick response. I have already gone through the above mentioned docs and sample but no where i am able to find an actual implementation of useFactory to mention SockJS URL (Just some sample snippet of webSocketFactory
available in docs). Additionally for your information i am using @stomp/ng2-stompjs: 8.0.0. (Note: Client module is auto imported from @stomp/stompjs.

Here you can find the full code

import {
  InjectableRxStompConfig,
  RxStompService,
  rxStompServiceFactory,
} from '@stomp/ng2-stompjs';
import { myRxStompConfig } from './rx-stomp.config';
import * as SockJS from 'sockjs-client'
import { Client } from '@stomp/stompjs';

const client = new Client();

..................

@NgModule({

providers: [
    {
      provide: InjectableRxStompConfig,
      useValue: myRxStompConfig,
      multi: true
    },
    {
      provide: InjectableRxStompConfig,
      useFactory: initWebSocketFactory,
      multi: true
    },
    {
      provide: RxStompService,
      useFactory: rxStompServiceFactory,
      deps: [InjectableRxStompConfig],
    }
]
})
export class AppModule {}

export function initWebSocketFactory(): Function {
  return client.webSocketFactory = () => new SockJS("https://some-domain.com/BASE_URL/ws");
} 

@kum-deepak
Copy link
Member

Your code should look like following:

import {
  InjectableRxStompConfig,
  RxStompService,
  rxStompServiceFactory,
} from '@stomp/ng2-stompjs';
import { myRxStompConfig } from './rx-stomp.config';
import * as SockJS from 'sockjs-client'

..................

@NgModule({

providers: [
     {
      provide: InjectableRxStompConfig,
      useFactory: initConfig,
      multi: true
    },
    {
      provide: RxStompService,
      useFactory: rxStompServiceFactory,
      deps: [InjectableRxStompConfig],
    }
]
})
export class AppModule {}

export function initConfig(): Function {
  const myRxStompConfig: InjectableRxStompConfig = {
    webSocketFactory: () => new SockJS("https://some-domain.com/BASE_URL/ws")
    // other settings
  }; 
  return myRxStompConfig;
} 

@benbabics
Copy link

benbabics commented Mar 10, 2021

For the life of me, I cannot get this to work. I am presented with the error:

Error: Uncaught (in promise): SyntaxError: Failed to construct 'WebSocket': The URL 'undefined' is invalid.
Error: Failed to construct 'WebSocket': The URL 'undefined' is invalid.

My init function looks like the following:

app.module

export function websocketInitConfig(): Function {
  const websocketConfig: any = {
    reconnectDelay: 1000,
    webSocketFactory: () => {
      return new SockJS( '/api/websocket' );
    },
    beforeConnect: client => {
      if ( !environment.websocket.isEnabled ) {  // evaluates as true
        client.deactivate();
      }
    },
    debug: msg => {
      console.log( '* debug websocket', msg );
    },
  };

  return websocketConfig;
}


@NgModule({
  imports: [
    CoreModule.forRoot({
      websocketFactory: websocketInitConfig,
    }),
    ...
  ]
  ...
})
export class AppModule { ... }

core.module (module where RxStomp lives)

export type CoreConfiguration = {
  websocketFactory: any;
};

@NgModule()
export class CoreModule {

  static forRoot(config: CoreConfiguration): ModuleWithProviders<CoreModule> {
    return {
      ngModule: CoreModule,
      providers: [
        {
          provide:    InjectableRxStompConfig,
          useFactory: config.websocketFactory,
          multi:      true,
        },
        {
          provide:    RxStompService,
          useFactory: rxStompServiceFactory,
          deps:       [ InjectableRxStompConfig ],
        },
      ]
    };
  }
}

@kum-deepak
Copy link
Member

Please attach your debug messages.

@benbabics
Copy link

benbabics commented Mar 11, 2021

Below is the single debug message:

* debug websocket  isConnected false

Error: Failed to construct 'WebSocket': The URL 'undefined' is invalid.
    at O._createWebSocket (main.082efa5588db27a43ff1.js:1)
    at O.<anonymous> (main.082efa5588db27a43ff1.js:1)
    at Generator.next (<anonymous>)
    at a (main.082efa5588db27a43ff1.js:1)
    at t.invoke (zone.js:391)
    at Object.onInvoke (main.082efa5588db27a43ff1.js:1)
    at t.invoke (zone.js:390)
    at e.run (zone.js:150)
    at zone.js:889
    at t.invokeTask (zone.js:423)
    at M (zone.js:831)
    at zone.js:741
    at a (main.082efa5588db27a43ff1.js:1)
    at t.invoke (zone.js:391)
    at Object.onInvoke (main.082efa5588db27a43ff1.js:1)
    at t.invoke (zone.js:390)
    at e.run (zone.js:150)
    at zone.js:889
    at t.invokeTask (zone.js:423)
    at Object.onInvokeTask (main.082efa5588db27a43ff1.js:1)

Turns out that when I remove multi: true, it works.

@kum-deepak
Copy link
Member

I do not understand the use of multi in Angular well. I have used it only with built-in tokens like APP_INITIALIZER.

I am closing the issue for now. If you are still facing the issue, please open a new ticket.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants