How to websocket in grails5 - grails

I want a grails application in which server sends some message at a fixed interval.
I have tried using spring-websocket plugin in grails, server and client are able to connect but that doesn't fullfill my requirement. i.e., I want, server sends some message at a fixed interval.
This is the server-side code :
package test
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.handler.annotation.SendTo
class ExampleController {
def index() { }
// server
#MessageMapping("/hello")
#SendTo("/topic/hello")
protected String hello(String world) {
List<String> list = new ArrayList<>();
BufferedReader file = new BufferedReader(new FileReader("src/main/resources/dummyLog.txt"));
file.eachLine {line ->
list.add(line)
}
int idx = (int)(Math.random() * list.size());
println idx;
return list.get(idx);
}
}
And this is the client-side code :
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', () => {
var socket = new SockJS("${createLink(uri: '/stomp')}");
var client = webstomp.over(socket);
client.connect({}, function() {
client.subscribe("/topic/hello", (message) => {
document.getElementById('helloDiv').append(message.body);
});
});
document.getElementById('helloButton').addEventListener('click', () => {
client.send("/app/hello", JSON.stringify("world"));
});
});
</script>
Thanks.

Related

How to respond with a stream in a Sveltekit server load function

Below I try to respond with a stream when I receive ticker updates.
+page.server.js:
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(ticker.price);
});
}
});
export async function load() {
return response????
};
Note: The YahooFinanceTicker can't run in the browser.
How to handle / set the response in the Sveltekit load function.
To my knowledge, the load functions cannot be used for this as their responses are JS/JSON serialized. You can use an endpoint in +server to return a Response object which can be constructed from a ReadableStream.
Solution: H.B. comment showed me the right direction to push unsollicited price ticker updates the client.
api route: yahoo-finance-ticker +server.js
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
/** #type {import('./$types').RequestHandler} */
export function GET({ request }) {
const ac = new AbortController();
console.log("GET api: yahoo-finance-ticker")
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(String(ticker.price));
}, { signal: ac.signal });
},
cancel() {
console.log("cancel and abort");
ac.abort();
},
})
return new Response(stream, {
headers: {
'content-type': 'text/event-stream',
}
});
}
page route: +page.svelte
<script>
let result = "";
async function getStream() {
const response = await fetch("/api/yahoo-finance-ticker");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
console.log("resp", done, value);
if (done) break;
result += `${value}<br>`;
}
}
getStream();
</script>
<section>
<p>{#html result}</p>
</section>

Signalr Client Generate error in Cross Domain

Access to XMLHttpRequest at 'https://domainname.com/signalr/negotiate?clientProtocol=2.1&connectionData=%5B%7B%22name%22%3A%22notificationhub%22%7D%5D&_=1654470068390' from origin 'https://domainname.com' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
this is my Hub Api server startup code
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJavaScriptProxies = true
};
map.RunSignalR(hubConfiguration);
});
this is my hub code
public class NotificationHub : Hub
{
public void Hello(string msg)
{
Clients.All.hello(msg);
}
public void JoinToArea(string name)
{
}
public override Task OnConnected()
{
var id = Context.ConnectionId;
return base.OnConnected();
}
}
this is my client code
<script src="~/Scripts/jquery.signalR-2.4.3.js"></script>
<script src="https://localhost:44312/signalr/hubs"></script>
<script>
$(document).ready(function () {
Connect();
});
var stockTickerHubProxy;
function Connect() {
$.connection.hub.url = 'https://localhost:44312/signalr';
stockTickerHubProxy = $.connection.notificationHub;
stockTickerHubProxy.client.foo = function () { };
stockTickerHubProxy.client.hello = function (msg) {
alert(msg);
}
if (stockTickerHubProxy) {
$.connection.hub.start().done(function () {
console.log("Connected...");
connectionId = $.connection.hub.id;
console.log(connectionId)
stockTickerHubProxy.server.joinToArea("OK");
stockTickerHubProxy.client.hello = function (msg) {
alert(msg);
}
})
.fail(function () {
alert("Can't connect");
})
;
$.connection.hub.disconnected(function () {
console.log("Server disconnected.");
});
$.connection.hub.reconnecting(function () {
console.log("Server reconnecting...");
});
$.connection.hub.reconnected(function () {
console.log("Server reconnected...");
Connect();
});
$.connection.hub.error(function (error) {
console.log('SignalR error: ' + error)
});
}
}
</script>
this work fine on localhost when i give hub api server host domain URL its generate this error. how can i fix this issue .

ASP.NET MVC SignalR Client methods not invoked in separate project

I have to two separate asp.net projects on the same server. All correct SignalR nuget packages are installed as far as I know on both projects. One is the ChatServer and another is a ChatClient. Both are DotNetNuke projects.
The second project DOES know about signalR because OnConnected in Server project gets triggered when running client project and the client user is inserted as online (through the server OnConnected method).
When the sever comes online (by launching server project)the method in the client "getonlineusers" does not get triggered though.
But if I refresh the client, the server does show as offline. But I shouldnt have to refresh.
I am probably missing something not sure if I am creating a proxy correctly for the client project, any help would be greatly appreciated.
How do you connect a client separate project to the project that has the hub with SignalR?
In the ChatServer project:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
public class ChatSupportHub : Hub
{
private ChatServerManager CSM { get; set; }
private int UserID { get; set; }
//private UserInfo CurrentUser { get; set; }
public ChatSupportHub()
{
CSM = new ChatServerManager();
//CurrentUser = DotNetNuke.Entities.Users.UserController.Instance.GetCurrentUserInfo();
}
public override Task OnConnected()
{
int outNumber = -1;
Online user = new Online();
User dbUser = new User();
var userid = Context.QueryString["userid"];
bool success = Int32.TryParse(userid, out outNumber);
string connID = Context.ConnectionId;
if (success)
{
user.ID = outNumber;
dbUser = CSM.GetUser(outNumber);
UserID = outNumber;
}
user.ConnectionID = connID;
user.Type = dbUser.TypeID.HasValue ? CSM.GetUserType((int) dbUser.TypeID) : null;
user.ShowOnline = true;
CSM.AddOnlineUser(user);
var onlineUsers = CSM.GetOnlineUsers();
var clients = Clients.Caller;
string onlineUserJSON = JsonConvert.SerializeObject(onlineUsers);
clients.getonlineusers(dbUser.Name, onlineUserJSON);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var username = Context.User.Identity.Name;
int userID = CSM.GetUserId(username);
CSM.RemoveOnlineUser(userID);
return base.OnDisconnected(stopCalled);
}
}
And the View index page of the ChatServer project:
<script src="~/myServertProjectPath/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
// set up the hub connection
var hub = $.connection.chatSupportHub;
$.connection.hub.qs = "userid=" + #Model.CurrentUserInfo.UserID.ToString();
hub.client.getonlineusers = function (currentUsername, onlineUsers) {
if (onlineUsers) {
console.log('There are users online and one is: ' + currentUsername);
}
}
$.connection.hub
.start()
.done(function () {
})
});
And same thing in the separate client project for the view:
<script src="~/myClientProjectPath/Scripts/jquery.signalR-2.4.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script>
$(function () {
connect();
});
function connect() {
hub = $.connection.chatSupportHub;
$.connection.hub.qs = "userid=" + #Model.CurrentUserInfo.UserID.ToString();
hub.client.getonlineusers = function (currentUsername, onlineUsers) {
if (onlineUsers) {
console.log ("Hello from the separate project.");
}
}
$.connection.hub
.start()
.done(function () {
})
}
</script>

MVC SignalR not firing from Controller Post Method

When Saving schedule to calendar it must auto update the activity logs on my notification bar in my Home Controller. It saves the data but only show when notification bar is refreshed. It seems that Hub is not starting when saved.
CalendarController.cs
[HttpPost]
public JsonResult SaveSchedule(Schedule s)
{
var userid = User.Identity.GetUserId();
var profile = _context.Profiles.Single(p => p.Id == userid);
var status = false;
if (s.Schedule_ID > 0)
{
//Update
var v = _context.Schedules.Where(a => a.Schedule_ID == s.Schedule_ID).FirstOrDefault();
if (v != null)
{
v.Shift = s.Shift;
}
}
var activitylog = new ActivityLog
{
UserId = userid,
LogDate = DateTime.Now,
Activity = ActivityHelper.GetActivityLog(4, profile.FirstName)
};
// save to data and must be shown on notification bar
_context.ActivityLogs.Add(activitylog);
_context.SaveChanges();
ActivityHub.StartLogging();
status = true;
return new JsonResult { Data = new { status = status } };
}
HomeController.cs
public JsonResult GetLogs()
{
return Json(ActivityHelper.GetActivityLogs(), JsonRequestBehavior.AllowGet);
}
ActivityHub.cs
public class ActivityHub : Hub
{
public static void StartLogging()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
//calls the signalR client part to execute javascript method
context.Clients.All.displayLog();
}
}
My CSHTML
<script>
$(function () {
var activityFromHub = $.connection.activityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
function FetchLogs() {
$.ajax({
type: 'GET',
url: '/Home/GetLogs',
datatype: 'json',
success: function (data) {
$("#logs tr").remove();
data = $.parseJSON(data);
if (data.length > 0) {
.... do some append here
}
},
error: function (error) {
alert("error");
}
});
}
});
</script>
ActivityHelper.cs
static readonly string connString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
public static class ActivityHelper
{
public static string GetActivityLogs()
{
string sqlCommand = #"my select query here";
try
{
var messages = new List<ActivityLog>();
using(var connection = new SqlConnection(connString))
{
connection.Open();
using (SqlConnection con = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
if(con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new ActivityLog
{
Activity = reader["Activity"] != DBNull.Value ? (string)reader["Activity"] : "",
LogDate = (DateTime)reader["LogDate"]
});
}
}
}
var jsonSerialiser = new JavaScriptSerializer();
var json = jsonSerialiser.Serialize(messages);
return json;
}
catch(Exception ex)
{
throw;
}
}
public static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= dependency_OnChange;
var activityHub = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
GetActivityLogs();
}
}
}
FIRST METHOD
First Solution change your javascript code like this. If this not works move to the second method:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
SECOND METHOD:
Each client connecting to a hub passes a unique connection id. You can retrieve this value in the Context.ConnectionId property of the hub context. And i found there is nothing happening like this. You may try this solution.
I think the simplest solution for your question is to use groups.
http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups
Your hub class would contain methods to join a group:
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
and your hub will be look like this:
public static void StartLogging(string groupName)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
context.Clients.Group(groupName).displayLog();
//calls the signalR client part to execute javascript method
//context.Clients.All.displayLog();
}
And change your javascript as like this:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
activityFromHub.server.joinGroup("Group1");
activityFromHub.server.StartLogging("Group1");
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
I hope this will resolve your issue. If you are still facing issue. Please leave comments. Thank you.

signalr client 400 bad request

I used signalr self hosting with MVC and need to call it from client on another machine so I wrote code like that:
$(function () {
jQuery.support.cors = true;
$.connection.hub.url = "http://[server external Ip]:3031/signalr";
var chat = $.connection.CustomHub;
chat.client.addMessage = function (data, IMEI) {
//SomeCode
}
}
Everything going well but I have this error in Firefox Firebug:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http:// [server external IP]/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22customhub%22%7D%5D&clientProtocol=1.3&_=1400692033406. This can be fixed by moving the resource to the same domain or enabling CORS.
You have to enable Cross-Domain on the server application by installing Microsoft.Owin.Cors package and the calling UseCors() when starting up SignalR (assuming you are using SignalR 2.x). You do NOT need to specify jQuery.support.cors = true; in SignalR 2.0, actually you should remove it AFAIK.
It seems that the error refer to the network connection, we have already used signalR and the identification of the Url to hub is not mandatory.
Below an implementation of SignalR for Sales object:
1- Enable the service broker on the Database SQL Server:
ALTER DATABASE BlogDemos SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE ;
2- Install signalR from nuget
Install-Package Microsoft.AspNet.SignalR
3- Add reference to signalr javascript library if not added via nuget
<script src="/Scripts/jquery.signalR-2.2.1.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
4- Add Javascript to call the Hub
$(function () {
// Declare a proxy to reference the hub.
var salesNotificationsHub = $.connection.salesHub;
// Create a function that the hub can call to broadcast messages.
salesNotificationsHub.client.updateClientsales = function () {
Search();
//alert('des nouveaux sales');
};
// Start the connection.
$.connection.hub.start().done(function () {
alert('successful connection');
}).fail(function (e) {
alert(e);
});
});
5- Add the Search function created in step 4
function Search() {
grid.reload({ searchString: searchfields });
}
6- Creation of the code to load the Grid from GetList function in the
Controller
grid = $("#grid").grid({
dataKey: "Codsales",
uiLibrary: "bootstrap",
dataSource:"/sales/GetList",
autoLoad: true,
columns: [
{ field: "Codsales", title: "Code Demande", width: 200, sortable: true },
{ field: "Libelsales", title: "Libellé Demande", width: 200, sortable: true },
],
pager: { enable: true, limit: 10, sizes: [10, 20, 30, 40] }
});
7- Creation GetList function in the controller
public JsonResult GetList()
{
List<salesData> objsalesList = GetList().ToList();
return Json(objGridData, JsonRequestBehavior.AllowGet);
}
8- Create Function GetList Where will be attached the SqlDependency Object
public static List<salesData> GetList()
{
SqlDependency dependency = new SqlDependency();
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (var command = new SqlCommand(#"SELECT Codsales, Libelsales, Datsales,DatDetailledsales FROM [dbo].sales ", cn))
{
command.Notification = null;
dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (cn.State == ConnectionState.Closed)
cn.Open();
command.ExecuteNonQuery();
}
}
List<salesData> objList = new List<salesData>();
objList=Fetchsales(filterExpression, sortExpression, pageIndex, pageSize, out total);
rowsCount = total;
return objList;
}
private static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
salesHub.Updatesales();
}
}
9- Create Hub class
public class salesHub : Hub
{
[HubMethodName("updatesales")]
public static void Updatesales()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<salesHub>();
context.Clients.All.updateClientsales();
}
}
10- Configuration of the Sqldependency in Global.asax file
protected void Application_Start()
{ //Start SqlDependency with application initialization
string connString= ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlDependency.Start(connString);
}
protected void Application_End()
{
//Stop SQL dependency
string connString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
SqlDependency.Stop(connString);
}
Cordially

Resources