Play 2.0 Comet using a persistant connection - scala

I am using play 2.0 for a realtime web application that connects to a backend socket in order to listen for data on that stream.
The following example does what I want but I don't know how to disconnect the socket if the clients webpage is closed or page has changed.
def comet = Action {
val out = Enumerator.imperative[String]()
val socketClient = new SocketClientModel("localhost", "testclient",
Option("username"), "password", out)
socketClient.listen
Ok.stream(out &> Comet(callback = "console.log"))
}
The problem that I am having is figuring out how to call socketClient.disconnect when the page has been closed or changed. Currently when I close the browser session I can still see the connection is established and data is being received on the server side.

A hacky solution could be for you to have a ping call in the other direction (client to server):
You setup a timer that will destroy the connection if it hasn't been canceled before 10 seconds (you can always setup a longer period):
var timer;
def comet = Action {
timer = Akka.system.scheduler.scheduleOnce(10 seconds) {
socketClient.disconnect
}
...
}
You setup an Action that receives pings and setup a timer in the javascript code to send requests to that route every 9 seconds:
def keepAlive = Action {
cancel.cancel
timer = Akka.system.scheduler.scheduleOnce(10 seconds) {
socketClient.disconnect
}
}
When the user goes to another page, or cancel the connection to the comet Action, then it should also cancel the ping timer on the client side (if the user change page, this should be automatic, but you will have to have some health check on the comet connection if you stay in the same page, etc.). When the client stops pinging your action, the timer will eventually kill the socket.
It's really ugly as you need to use a var that is shared between your two actions and it's not really thread safe. It also would only be able to support one client. But the code example you have given is the same I guess. You would need to track which session has open which socket to support multiple client. You would loose stateless behaviour though.
A cleaner solution would be to use a small actor system to encapsulate this behaviour:
- you have a SocketActor that, when it receives a Start message, opens a socket and pushes things on a PushEnumerator, it keeps doing this as long as it doesn't receive a Stop message.
- you also have a KeepAliveActor that will after a given amount of time send a Stop message to the SocketActor, unless it has received a KeepAlive message before (so, what's going on here).
When you receive a comet connection, you spawn two new actors (you should have a manager actor to hide the two actors and relay the messages, etc.) and keep a ref to them linked to the session. The keepalive action would then fetch the ref for the right session and send KeepAlive messages to the actor.
The SocketActor would, when it receive a Stop message close the socket and destroy the actors that were linked to him.
It's a bit more involved coding, so I am not including snippets, but it would be a simple AKKA system, so you should be able to set it up by checking out the AKKA tutorials if you are not yet used to it.

Related

Is there a smart possibility to get API results without sending requests every second? [VueJS | Vuetify]

So I made a website to show which services on my server are running and which are offline.
The site is an Vuetify App running in a docker container. My services are monitored via UptimeRobot.
Currently I use:
created: function () {
this.interval = setInterval(() => this.getStatuses(), 1000);
},
To trigger my API request function every second to update the status of my services.
But is there some smarter possibility to only update on change and not request every second to see if something happened?
Like I send one request to get the status and then receive a message when something changed? I hope you can understand, whats my problem. It's hard to decribe.
Yes you can by firing an event. for example:
in your app.js
window.Fire = new Vue();
For example here you create a user then you want to update table after creating a new user, Follow these steps:
createUser(){
// FireUpdate is your fire name, you can give it any name you want!
// Call this after you post something to specific route.
Fire.$emit('FireUpadte');
}
Then you will load new users using this approach:
created(){
// Load new Users after created.
Fire.$on('FireUpadte', () => { this.createUser(); });
}
For more information check this video: https://www.youtube.com/watch?v=DHuTkJzH2jI&list=PLB4AdipoHpxaHDLIaMdtro1eXnQtl_UvE&index=20
What you're looking for are websockets. You establish a websocket connection and it stays open, allowing the server to notify the web app when something changes.
You can run your own socket.io server on a Node.js backend or use a service like Pusher.com (very cheap, free tier is pretty big).
I highly recommend going the Pusher.com route, they also have great tutorials ; )
https://pusher.com

How to synchronize backend timer with mobile app

I am developing a app that chooses a user and has a 15 sec. timer for that user to respond. The user app queries the db every 5 sec to see if that user is chosen. If so The mobile app begins a 15 sec. timer. The problem is that the timers will never match up because the user app can be on a different timer cycle that the backend and can query the db at a later time. I use Angular, NodeJS, and expressJS + MongoDB to develop this app.
any suggestion on how I can get the timers to be synchronized?
You don't need to have two timers running at the same time. Start a timer on the front-end, then let the back-end know when the timer on the front-end started. As long as you treat one side as the single source of truth, the other side will be able to infer whether the timer has finished or not.
Although the timers might not be synchronized, time should be the same everywhere. If the back-end knows that the front-end timer started at 12:01:00, it also knows that the timer will end at 12:01:15. In this way, it can just continue to check whether the current time is before or after 12:01:15.
This is definitely a job for websockets! Websockets unlike HTTP enable two way data flow so your server and client can talk in real-time. Socket.io is a really popular framework for enabling this type of interaction and hooks really seamlessly into node/express.
http://socket.io/
Here is a tutorial to get your started http://socket.io/get-started/chat/.
The basic flow will be
Open a socket between the user and the server.
When the user is chosen (I assume on the server-side) then do a io.emit('user chosen', { userId: '<the user id>' });. This will send a message over the socket to all attached applications.
Start the timer on the server and send info that the period is over. Something like this should work. setTimeout(() => socket.emit('user chosen end', { userId: '<the user id>' }), 15000);
In your app you will be listening for the 'user_chosen' event and can check if the logged in user has the same id as the one sent over the socket. If the user id's match enable the text input for the user to set the input. Something like this: socket.on('user chosen', function(msg){ /* Enable the input */ });
The app will also be listening for the 'user_chosen_end' event and if the ids of the user again match, disable the text input or do whatever else you need to do. Again this will look like: socket.on('user chosen end', function(msg){ /* Disable the input & do anything else */ });
Hope this helps :)

Web Sockets - Send messages to all clients

I am completely new to Web Sockets, I have applied them to my chat so now it looks something like this:
<script type='text/javascript'>
var connection = new WebSocket("ws://echo.websocket.org"); //"public" websocket server
connection.onopen = function () {
console.log('Connection: OK.');
};
connection.onerror = function (error) {
console.log(Error: ' + error);
};
connection.onmessage = function () {
$('#chatbox').load('/chatbox.php');
};
$(document).ready(function() {
$('#chatOK').click(function(event) {
//something
connection.send('Get new messages.');
});
});
</script>
It works well for one client, when I enter message it updates the chatbox but only mine, but I want to update it for everyone (=all users who have opened chat window). So how can I send with WebSockets message to all clients?
You cannot do this with the websocket.org echo server - this just echos whatever you send it back to the sender.
You need a server which handles the distribution to all clients. This is easiest done using the Publish & Subscribe messaging pattern:
All chat messages are published to a common topic, e.g. "myChatRoom_1".
All connected clients indicate to the server that they are interested in messages to "myChatRoom_1" (that's the subscription).
Now when the server receives a published event for "myChatRoom_1" it can distribute it to all subscribers.
Take a look at this demo to see this in action.
It's based on Crossbar.io, an open source application router, which does PubSub out of the box, without an additional backend. So if you used this, you'd need to implement just the changes to your browser chat clients.
If you want to integrate with your PHP backend (e.g. since this stores the chat messages somewhere), there is a PHP library for connecting to Crossbar.io as well.
Full disclosure: I work for Tavendo, who are the maintainers of the Crossbar.io project.

in-addr.arpa. responses not triggering callbacks in ServiceListener

I am trying to setup some ServiceListeners, in particular two:
zeroConf.addServiceListener("100.1.168.192.in-addr.arpa.", myListener);
zeroConf.addServiceListener("_workstation._tcp.local.", myListener);
Whenever I do this, I get callbacks for myListener on serviceResolved() and serviceAdded() for all services that match "_workstation._tcp.local." However, I get no callbacks for "100.1.168.192.in-addr.arpa." ... despite the fact that jmDns sends out the queries, and a response comes back! I've attached a tcpdump of the request packets that jmdns sends out, and the response that comes back for it. However, the callbacks are not called so I never see the response in my application.
Does anyone know why this might be happening?
http://users.ece.cmu.edu/~gnychis/jmdns_nocallback.pcap
After some debugging of the actual event type that comes in, the event type resolves to "_tcp.in-addr.arpa." Adding this to my service listeners triggers the call back.

How to kill a GWT RPC which has not yet completed

My code is for sending Emails to multiple users.User will click on send button,and rpc will be called. Now if user clicks on Cancel button .Ongoing rpc should be cancelled. . Can anyone help ?
I googled a lot, they have introduced the concept of Request Builder. But I am not getting any perfect idea.
Make your async method return a Request instead of void so you can call cancel() on it.
For the same reason, asynchronous methods do not have return types; they generally return void. Should you wish to have more control over the state of a pending request, return Request instead.
— Source: https://developers.google.com/web-toolkit/doc/latest/DevGuideServerCommunication#DevGuideCreatingServices
FYI, you can also use RequestBuilder as the return type, you'll then have to call the send() method by yourself (after possibly customizing the request, e.g. adding headers) to actually make the request to the server.
And of course, if you need to tell the server to abort the processing, you'll have to make another RPC call.
The request is asynch, so the client side can do anything it wants.
All you need to do is add a flag to indicate that the request should be cancelled, and then change the onSuccess method to check the flag and do nothing if it is set.
You should clear the requestCancelled flag each time you make a request - or else after the first request is cancelled, you won't be able to make another one...
e.g.
boolean requestCancelled = false;
void onSuccess(...)
{
if (!requestCancelled) {
// actual response handing code
}
}
If you really want to cancel the request on the server side, it is a lot more complicated. You could do this by sending a second request - one where the fuinctionality is to cancel a request.
To make this work, the "cancel request" has to set a field somewhere the "email request" can read. The "email request" needs to check if the "cancel field" has been set.
// server side Impl
void cancelRequest()
{
// You need to implement this class and ensure it really is a singleton
// and thread safe.
RequestStatusSingleton.setCancelled(true);
}
void serverSideEmailFunc()
{
while(modeEmailAddrs && ! RequestStatusSingleton.getCancelled()) {
// get next address and send email
}
}
Obviously this is a lot of work. Have you considered:
Not having a cancel button on your GUI?
Getting the server to process emails a few at a time (i.e. client sends multiple requests until server tells the client all emails are done).
I totally understand your user. No one wants to wait for 15 seconds.
There is no standard way to "kill" the request, because there is no way to know where your server/datastore is in implementing it. Unless you deal with a process that can be put in a single transaction that can be rolled back, you will have to implement your own logic. For example, if you asked the server to save an entity, you will have to tell the server to save this entity again, but this time without the changes.
Also, think again about your use case. Why a user wants to kill the request? May be he simply wants to go to another place in the app. Then there is no need to kill the request: when the response arrives, check if the user is still in the same place patiently waiting. If not, do not execute onSuccess().