SignalR opens new connection for the same user - asp.net-mvc-3

I am new to SignalR and trying to implement long running result pooling. I have added JS to my ASP.NET MVC app and created bug class.
JS
<script type="text/javascript">
var message= $.connection.messageHub;
$(function () {
message.addMessage = function (htmlstring) {
alert(htmlstring);
};
$.connection.hub.start(function () {
message.longRunningMethod('#HttpContext.Current.Session.SessionID');
});
});
</script>
c# code
[HubName("messageHub")]
public class MessagesHub : Hub
{
public void longRunningMethod(string sessionId)
{
var repeatChecking = 0;
while (repeatChecking < 3000000)
{
Caller.addMessage("Test");
repeatChecking++;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}
The code works fine but there is one problem. Every time the same user refresh web page new Hub class is created and new longRunningMethod method is executed. I would like to resume connection and attach to the same hub instance and resume receiving messages. Could anyone explain how to implement this?

Hubs are created and destroyed very frequently so never put any data that you expect to last on them (unless it's static).
I'm not quite sure why you're looking to have a long running method that can take in data (because SignalR is always up and available to take in/handle data) but here's how you can do it:
Checkout the SignalR stock ticker example (you can pull it in via Nuget). It creates a single instance class that fires up a timer. That timer is used to broadcast data down to the clients. https://github.com/SignalR/SignalR-StockTicker
You can also check out ShootR. It's a multiplayer game built with SignalR that does much of the same. Creates a background timer that acts as the game loop on the server and then pushes data down to clients. https://github.com/NTaylorMullen/ShootR
Ultimately your solution will involve either making a singleton or a static timer that acts as your "long" running method.
Hope this helps!

Related

Can we use server sent events in nestjs without using interval?

I'm creating few microservices using nestjs.
For instance I have x, y & z services all interconnected by grpc but I want service x to send updates to a webapp on a particular entity change so I have considered server-sent-events [open to any other better solution].
Following the nestjs documentation, they have a function running at n interval for sse route, seems to be resource exhaustive. Is there a way to actually sent events when there's a update.
Lets say I have another api call in the same service that is triggered by a button click on another webapp, how do I trigger the event to fire only when the button is clicked and not continuously keep sending events. Also if you know any idiomatic way to achieve this which getting hacky would be appreciated, want it to be last resort.
[BONUS Question]
I also considered MQTT to send events. But I get a feeling that it isn't possible for a single service to have MQTT and gRPC. I'm skeptical of using MQTT because of its latency and how it will affect internal message passing. If I could limit to external clients it would be great (i.e, x service to use gRPC for internal connections and MQTT for webapp just need one route to be exposed by mqtt).
(PS I'm new to microservices so please be comprehensive about your solutions :p)
Thanks in advance for reading till end!
You can. The important thing is that in NestJS SSE is implemented with Observables, so as long as you have an observable you can add to, you can use it to send back SSE events. The easiest way to work with this is with Subjects. I used to have an example of this somewhere, but generally, it would look something like this
#Controller()
export class SseController {
constructor(private readonly sseService: SseService) {}
#SSE()
doTheSse() {
return this.sseService.sendEvents();
}
}
#Injectable()
export class SseService {
private events = new Subject();
addEvent(event) {
this.events.next(event);
}
sendEvents() {
return this.events.asObservable();
}
}
#Injectable()
export class ButtonTriggeredService {
constructor(private readonly sseService: SseService) {}
buttonClickedOrSomething() {
this.sseService.addEvent(buttonClickedEvent);
}
}
Pardon the pseudo-code nature of the above, but in general it does show how you can use Subjects to create observables for SSE events. So long as the #SSE() endpoint returns an observable with the proper shape, you're golden.
There is a better way to handle events with SSE of NestJS:
Please see this repo with code example:
https://github.com/ningacoding/nest-sse-bug/tree/main/src
Where basically you have a service:
import {Injectable} from '#nestjs/common';
import {fromEvent} from "rxjs";
import {EventEmitter} from "events";
#Injectable()
export class EventsService {
private readonly emitter = new EventEmitter();
subscribe(channel: string) {
return fromEvent(this.emitter, channel);
}
emit(channel: string, data?: object) {
this.emitter.emit(channel, {data});
}
}
Obviously, channel can be any string, as recommendation use path style.
For example: "events/for/<user_id>" and users subscribed to that channel will receive only the events for that channel and only when are fired ;) - Fully compatible with #UseGuards, etc. :)
Additional note: Don't inject any service inside EventsService, because of a known bug.
#Sse('sse-endpoint')
sse(): Observable<any> {
//data have to strem
const arr = ['d1','d2', 'd3'];
return new Observable((subscriber) => {
while(arr.len){
subscriber.next(arr.pop()); // data have to return in every chunk
}
if(arr.len == 0) subscriber.complete(); // complete the subscription
});
}
Yes, this is possible, instead of using interval, we can use event emitter.
Whenever the event is emitted, we can send back the response to the client.

using signalR .net core client

I have set up a signalR website .net core. My function in my hub is:
public async Task Notify(int id) {
await Clients.All.InvokeAsync("Notified", id);
}
I have also tested this with the following js:
let connection = new signalR.HubConnection(myURL);
connection.on('Notified', data => {
console.log(4, data);
});
connection.start();
The js code seems to work fine and I see the log when I try connection.Invoke('Notify').
Now I have a console app that can needs to make the invoke. I am trying this in two ways and don't mind either solution:
1. A mvc controller within the signalR website that can take the id and invoke 'Notified'.
2. Use the client library Microsoft.AspNetCore.SignalR.Client in the console app.
The way 1 I have only done in classic asp.net like this:
GlobalHost.ConnectionManager.GetHubContext(hubName)
But couldn't find a way to do this in .net core.
Way 2 I have used the library and tried this so far:
var con = new HubConnectionBuilder();
con.WithUrl(myURL);
var connection = con.Build();
connection.InvokeAsync("Notify",args[0]).Wait();
This is the closest I have come to create a connection in the same way as the js code. However this code throws a null pointer when calling connection.InvokeAsync. The connection object is not null. It seems to be an internal object that is null. According to the stack trace the exception is thrown when a MoveNext() function is internally called.
Well looks like both are not currently possible. As of now I just used a forced way which is hopefully temporary.
I have created and used the following base class for hubs:
public abstract class MyHub : Hub
{
private static Dictionary<string, IHubClients> _clients = new Dictionary<string, IHubClients>();
public override Task OnConnectedAsync()
{
var c = base.OnConnectedAsync();
_clients.Remove(Name);
_clients.Add(Name, Clients);
return c;
}
public static IHubClients GetClients(string Name) {
return _clients.GetValueOrDefault(Name);
}
}
GlobalHost is gone. You need to inject IHubContext<THub> like in this sample.
This can be a bug in SignalR alpha1. Can you file an issue on https://github.com/aspnet/signalr and include a simplified repro?

How to send message or establish 2 way communication between two XUL Overlay Firefox extensions/add-ons

I have an XUL Overlay Firefox extension, I need to develop a dummy XUL extension that establishes connection with the original extension and sends a set of parameters (message) to the original extension. In short, I have to trigger my original extension with my dummy extension.
Probably the easiest way to do this is to have the original extension listening for a custom event on the base browser window. The dummy extension can then create and dispatch the event with whatever custom data is desired.
Creating and dispatching the event from the dummy:
function sendDataToMainExtension(data) {
if (typeof window === "undefined") {
//If there is no window defined, get the most recent.
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
}
//This assumes that this event is being both sent from
// and received by privileged (main add-on) code.
var event = new CustomEvent('MyExtensionName-From-Dummy', { 'detail': data });
window.dispatchEvent(event);
}
You may need to take the same steps for making sure the data is visible on the receiving end as would be necessary when firing from privileged code to non-privileged code.
Listening for the event in main:
Components.utils.import("resource://gre/modules/Services.jsm");
const Ci = Components.interfaces;
//Listen for the event on all windows as it is unknown on which one
// the event will be sent.
function loadIntoWindow(myWindow) {
myWindow.addEventListener("MyExtensionName-From-Dummy",
receiveMessageFromDummy, false);
}
function unloadFromWindow(myWindow) {
myWindow.removeEventListener("MyExtensionName-From-Dummy",
receiveMessageFromDummy, false);
}
function forEachOpenWindow(fn) {
// Apply a function to all open browser windows
var windows = Services.wm.getEnumerator("navigator:browser");
let windowCount =0;
while (windows.hasMoreElements()) {
windowCount++;
fn(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
}
}
function receiveMessageFromDummy(event) {
var dataFromDummy = event.detail;
//Do whatever was desired with the data.
}
var WindowListener = {
onOpenWindow: function(aWindow)
{
let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
function onWindowLoad()
{
domWindow.removeEventListener("load",onWindowLoad);
if (domWindow.document.documentElement.getAttribute("windowtype")
== "navigator:browser") {
loadIntoWindow(domWindow);
}
}
domWindow.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { }, // Each window has an unload event handler.
onWindowTitleChange: function(xulWindow, newTitle) { }
};
//Listen for the custom event on all current browser windows.
forEachOpenWindow(loadIntoWindow);
//Listen for the custom event on any new browser window.
Services.wm.addListener(WindowListener);
The data sent should be available as event.detail within the receiveMessageFromDummy() function.
The code above provides one way communication. Two way communication is obtained just duplicating the code to communicate in the other direction with a different custom event. In other words, by having the main extension dispatching a different custom event called something like MyExtensionName-From-Main and having the dummy extension listening for that event. The code is exactly the same as above, but with the event name changed and the function called being receiveMessageFromMain().
Alternately, you could use Window.postMessage(). Doing so sends a "message" event for which you can listen. However, doing so leads to complications which are easier to avoid by using a custom event (e.g. you have to account for the fact that any code (i.e. some other random extension) could be using this event for their own purpose).
Note: The code to loop through windows was originally taken from Converting an old overlay-based Firefox extension into a restartless addon which that author re-wrote as the initial part of How to convert an overlay extension to restartless on MDN. It has been modified multiple times from that code. It may have even earlier versions from other sources.

SignalR: Task Join() is not called

I'm trying to create a chat application using SignalR. To make it possible to send private messages I want to assign clients to a group with the name of their profileID. So I can simply call the addMessage function of the group to send to a specific client.
When I go to this page: https://github.com/SignalR/SignalR/wiki/Hubs
It tells me to add a function to the Hub called Join(). In here I can add the incomming client to a group. So I created this code:
[HubName("Chat")]
public class ChatHub : Hub
{
public Task Join()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Profiel_DataHelper profiel = new Profiel_DataHelper(HttpContext.Current.User.Identity.Name);
return Groups.Add(Context.ConnectionId, profiel.ProfielID.ToString());
}
else
{
return null;
}
}
.....
When I want to call a specific client I use this code:
var context = GlobalHost.ConnectionManager.GetHubContext();
context.Clients.Group(profielidNaar).addTyptOnline(profielidVan);
But when I run the program the Join() Task is not being called at all, therefore my call to the group is also not working.
What am I doing wrong?
Join is a method on your hub that you need to call from the client. Nobody is going to call it for you and "Join" isn't a special method that gets called automatically. The documentation is showing you how to declare method that "can" be called from the client.
There's other ways to know when clients connect, reconnect and disconnect and it's detailed here:
https://github.com/SignalR/SignalR/wiki/Hubs#detecting-connect-reconnect-and-disconnect-clients-in-hubs

How can a JSF/ICEfaces component's parameters be updated immediately?

I have an ICEfaces web app which contains a component with a property linked to a backing bean variable. In theory, variable value is programmatically modified, and the component sees the change and updates its appearance/properties accordingly.
However, it seems that the change in variable isn't "noticed" by the component until the end of the JSF cycle (which, from my basic understanding, is the render response phase).
The problem is, I have a long file-copy operation to perform, and I would like the the inputText component to show a periodic status update. However, since the component is only updated at the render response phase, it doesn't show any output until the Java methods have finished executing, and it shows it all changes accumulated at once.
I have tried using FacesContext.getCurrentInstance().renderResponse() and other functions, such as PushRenderer.render(String ID) to force XmlHttpRequest to initialize early, but no matter what, the appearance of the component does not change until the Java code finishes executing.
One possible solution that comes to mind is to have an invisible button somewhere that is automatically "pressed" by the bean when step 1 of the long operation completes, and by clicking it, it calls step 2, and so on and so forth. It seems like it would work, but I don't want to spend time hacking together such an inelegant solution when I would hope that there is a more elegant solution built into JSF/ICEfaces.
Am I missing something, or is resorting to ugly hacks the only way to achieve the desired behavior?
Multithreading was the missing link, in conjunction with PushRenderer and PortableRenderer (see http://wiki.icesoft.org/display/ICE/Ajax+Push+-+APIs).
I now have three threads in my backing bean- one for executing the long operation, one for polling the status, and one "main" thread for spawning the new threads and returning UI control to the client browser.
Once the main thread kicks off both execution and polling threads, it terminates and it completes the original HTTP request. My PortableRenderer is declared as PortableRender portableRenderer; and in my init() method (called by the class constructor) contains:
PushRenderer.addCurrentSession("fullFormGroup");
portableRenderer = PushRenderer.getPortableRenderer();
For the threading part, I used implements Runnable on my class, and for handling multiple threads in a single class, I followed this StackOverflow post: How to deal with multiple threads in one class?
Here's some source code. I can't reveal the explicit source code I've used, but this is a boiled-down version that doesn't reveal any confidential information. I haven't tested it, and I wrote it in gedit so it might have a syntax error or two, but it should at least get you started in the right direction.
public void init()
{
// This method is called by the constructor.
// It doesn't matter where you define the PortableRenderer, as long as it's before it's used.
PushRenderer.addCurrentSession("fullFormGroup");
portableRenderer = PushRenderer.getPortableRenderer();
}
public void someBeanMethod(ActionEvent evt)
{
// This is a backing bean method called by some UI event (e.g. clicking a button)
// Since it is part of a JSF/HTTP request, you cannot call portableRenderer.render
copyExecuting = true;
// Create a status thread and start it
Thread statusThread = new Thread(new Runnable() {
public void run() {
try {
// message and progress are both linked to components, which change on a portableRenderer.render("fullFormGroup") call
message = "Copying...";
// initiates render. Note that this cannot be called from a thread which is already part of an HTTP request
portableRenderer.render("fullFormGroup");
do {
progress = getProgress();
portableRenderer.render("fullFormGroup"); // render the updated progress
Thread.sleep(5000); // sleep for a while until it's time to poll again
} while (copyExecuting);
progress = getProgress();
message = "Finished!";
portableRenderer.render("fullFormGroup"); // push a render one last time
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
}
});
statusThread.start();
// create a thread which initiates script and triggers the termination of statusThread
Thread copyThread = new Thread(new Runnable() {
public void run() {
File someBigFile = new File("/tmp/foobar/large_file.tar.gz");
scriptResult = copyFile(someBigFile); // this will take a long time, which is why we spawn a new thread
copyExecuting = false; // this will caue the statusThread's do..while loop to terminate
}
});
copyThread.start();
}
I suggest looking at our Showcase Demo:
http://icefaces-showcase.icesoft.org/showcase.jsf?grp=aceMenu&exp=progressBarBean
Under the list of Progress Bar examples is one called Push. It uses Ajax Push (a feature provided with ICEfaces) to do what I think you want.
There is also a tutorial on this page called Easy Ajax Push that walks you through a simple example of using Ajax Push.
http://www.icesoft.org/community/tutorials-samples.jsf

Resources