Cometd : Multiple tabs for online-users management creating false online-status - java

I am working on a Spring-MVC based web-app which uses Cometd for chat purposes. For real-time management of which user is online, we are sending notifications when the user is online. So when window is closed, then notifications don't appear and after 30 seconds it is determined that the user is offline or not reachable.
Now the problem happens when user is over multiple browsers. Lets just keep it for 2 now. So, after 10 minutes we are setting user status to 'Away from Keyboard'(AFK). But if the user is online in one browser, then we are getting a blinking status, for few seconds because of the browser in 'Idle Mode', we get a AFK, and from the active machine we get an 'Available' status.
How can we solve this problem? Any ideas, suggestions. I thought of using a boolean flag, and couple with IP address, which will be checked before overwriting the notification, but it has a problem of stale notifications.
This is my code for sending out notifications for online to all listeners(Friends of user).
Code :
#Listener(value = "/service/online")
public void OnlineNotifications(ServerSession remote, ServerMessage.Mutable message) {
Person sender = this.personService.getCurrentlyAuthenticatedUser();
Map<String, Object> input = message.getDataAsMap();
String onlineStatus = (String) input.get("status");
Map<String, Object> output = new HashMap<>();
output.put("status", onlineStatus);
output.put("id", sender.getId());
ServerChannel serverChannel = bayeux.createChannelIfAbsent("/online/" + sender.getId()).getReference();
serverChannel.setPersistent(true);
serverChannel.publish(serverSession, output);
}
Any ideas are welcome. Thanks a lot. :-)

Your application can easily associate multiple devices with the same userName.
You can do this from your SecurityPolicy, just associate the userName string with a List<ServerSession> or whatever data structure fits better your case.
Your online state for a particular userName is therefore a function of all the ServerSessions of this particular userName.
As such, it's a mapping from the userName to a list of online states.
If you imagine user bob logged in from two browsers, the mapping for its online status can be:
"bob" -> ["afk", "online"]
To compute the online status for bob you just run through all the online statuses for each ServerSession and if there is at least one online then it's online, if all are afk then it's away from the keyboard, and so on.
You can implement this logic in several ways (e.g. storing the online status as a ServerSession attribute, use a different data structure, cache the per-user online status, etc.), but you get the idea.
Whenever you get an online status change from one ServerSession, you update your server-side data structure, and then recompute the online status from all ServerSessions for that user, and that is what you send to the other users.

Related

Android/Java: how to send notifications to user, even when app is not "actively" being used?

I want to be able to send a notification to a user IF something changes.
For example, my application is crime-related. So users can submit reports of crimes that have happened in their neighborhoods.
When a new crime is reported, I want to be able to send ALL users in that specific neighbourhood a notification, even if they are not actively using the app.
How can this be done? I'm quite new at this but to my understanding services like Firebase Messaging require you to type out a message manually and select users to send the message to manually. I'm wondering if there's a way this can be done without someone having to manually do work?
Similar to how snapchat/instagram and stuff will send you notifications that someone has sent you a message even when you are not using your phone.
In my case, I just want the same standard notification "New crime in your area" to be displayed...
How can I do this? (Currently for notifications I'm just using Notification Channels), thank you so much!
You can easily do this using Parse Server through FCM integration.
First, you need to setup your Android app to be able to receive push notifications
Just follow this Quickstart: https://docs.parseplatform.org/parse-server/guide/#push-notifications-quick-start
Second, you need to create a cloud code function
I suggest you to create a cloud code function that will receive the neighborhood as parameter, will query for the user installations in that neighborhood and send the push notification to all of them.
It would be something like this:
Parse.Cloud.define('notifyCrime', async req => {
const query = new Parse.Query(Parse.Installation);
query.equalTo('neighborhood', req.params.neighborhood); // I'm supposing you have a field called neighborhood in your installation class - if not, you can save this field there when the user sign up
await Parse.Push.send({
where: query,
data: {
alert: 'There is a crime in your neighborhood'
},
useMasterKey: true
});
});
Reference: https://docs.parseplatform.org/js/guide/#sending-pushes-to-queries
Third, you need to call the cloud function from your Android app
Once some user has reported a crime, you can call the cloud code function that you created in step 2 to notify all other users in the same neighborhood.
It would be something like this:
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("neighborhood", "The neighborhood goes here");
ParseCloud.callFunctionInBackground("notifyCrime", params, new FunctionCallback<Object>() {
void done(Object response, ParseException e) {
if (e == null) {
// The users were successfully notified
}
}
});
Reference: https://docs.parseplatform.org/cloudcode/guide/#cloud-functions
"my understanding services like Firebase Messaging require you to type out a message manually and select users to send the message to manually".
This is not completely true. There is a method name Firebase Topic Messaging, that lets you send notifications to specific user segments only. You have to register from the app for that topic and then, you can send customized message to your user groups based on topics they subscribed to.

Send data to another server when it's updated

I am writing a service which would store picture associated with registered email. So, other domains would have a possibility to get image of the user by email. The main goal is not to upload it each time as nowadays we have to register almost everywhere and that process is quite annoying.
My application is written on Java and I am using REST API.
For example, user's account information is available by login:
#RequestMapping(value = "/get/{login}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<User> getByEmail(#PathVariable String login) {
User user = userDao.getUserByLogin(login);
return Optional.ofNullable(user)
.map(result -> new ResponseEntity<>(
result, HttpStatus.OK))
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
And now, what i want is to send just updated data to the domains which gonna use my service. How could I figure that out? I think I could ask "domain" to provide some information in order to use my service (some king of registration), but what exactly should I ask for to be able to send data udpdates?
In my thoughts they should also provide some REST path where I could send some kind of request that something has changed.
Any help would be appreciated a lot, thanks.
This is essentially a pub-sub model . You publish some information , on various defined events , to whoever has subscribed to it . Look at this as a subset of state syncronisation of the user information across various endpoints.
In your case , the 'domains' you are referring to would be subscribers of your service and the events could be 'itemAdded' , 'itemAdded' etc. You would want to 'push' out the updates ( or whole info) to the subscribers when the event they have subscribed for occurs , instead of them trying to pull this at some frequency ( that would be a lot of waste calls to your server - you dont want that ! )
There are various solutions available that could achieve this . The one I am going to point you to is called Twilio Sync . This would obviously mean that the 'domains' would have to do some changes at their end to subscribe and consume the updates , but I dont see how else could they be regularly updated if they want information pushed.
Send last update date to the endpoint from the domain which
use it. Then check which data was updated after that date and return
appropriate response.
Talking about image, you can always return URL for download but add last update field. The service which use REST service will determine to download it or not.
Also you may need event driven messaging, publish–subscribe pattern (https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern). Related threads:
How would I create an asynchronous notification system using RESTful web services?
Event Based interaction style in REST
Firebase for mobile apps: https://firebase.google.com/docs/notifications/

Wowza - onHTTPCupertinoStreamingSessionCreate called multiple times

I'm trying to limit streams per user, and my idea was to store info does user stream video or not into database. Then when session is created I would check database and approve/reject stream.
My problem is that onHTTPCupertinoStreamingSessionCreate is fired multiple times. I have no idea why or how it's even possible. This is my logic in short:
public void onHTTPCupertinoStreamingSessionCreate(HTTPStreamerSessionCupertino httpSession) {
if( alreadyStreaming( userID ) ){
httpSession.rejectSession();
return;
}
else{
setStreamActiveInDB( userID, true);
}
}
public void onHTTPCupertinoStreamingSessionDestroy(HTTPStreamerSessionCupertino httpSession) {
setStreamActiveInDB( userID, false );
}
Any idea on how to check why is this event firing multiple times, or another idea how to limit number of streams per user?
I have also faced this issue and one particular scenario was that the HTTP streaming link was in a mobile browser. The user clicks on the link, the browser does not know the content type, so it connects which causes a new HTTP session and thus a call to onHTTPCupertinoStreamingSessionCreate. Then the browser gets the response and understands that this is a video, so it launches a player. The player asks for the manifest, that is a second HTTP session. I have seen that a third session is launched when the player starts loading the video chunks. Then the first 2 sessions die eventually, and the third one survives. I had to do various tricks to connect these sessions and account them as one session. I hope this gives you an idea why this happens. So a new HTTP session is not equal to a new connected player in general.

Preventing Multiple Login with same login credentials

I am developing a web application that needs to prevent multiple login using the same user name and password concurrently.
If it happens on the same machine then we need to do something with the user session, but it should also prevent if they are login on different machines using the same user name and password.
What can be the best approach :-
1) should i store the user session,credentials,and IPAddress of the machine in the DB.
2) should we use the session tracking mechanism in the application itself.If so what is the best approach?
Also , We have to keep following things in mind:
1) If user close the browser without logout.
2) If session times out.
Hope it clears the question.
Besdies data base hits (which could fail if your server is broguth down without updating db) : A data base friendly way is not to hit the data base for every re login or you could get denial of service attacks that brig you dowm. Instead implement a session listener in J2EE and check if same user is logged in by looking up the user id in a cache.
If you have more than one app node then need a distributed cache with a time out same as session time out in web.xml.
Simply have a field in your database that has text that says online or offline for each user, according to whether they are logged in or not. So when someone tries to log in with that username, check the database if the field says online for that given user on submit. If the field says online, don't allow log in. Otherwise permit it.
without using a database
you can store if a user is online in a text file
$check= "onlineCheck.txt";
$fh = fopen($check, 'a') or die("can't open file");
$nowOnline= "USER678 \n";
fwrite($fh, $nowOnline);

Getting session data of logged in users in java

I am working with servlets. When my user logs in, my application is setting his userId and deviceId in a session variable.
Some other part of my application now needs the list of online people and their userId/ deviceId. Note that a user may have simultaneous logins with 2 devices, so we have 2 deviceIds for the same userId.
How should I approach this situation ? I am not willing to use database for tracking the login/ logout functionalities
Store this information also in HttpServletContext's attribute. This context is global per servlet and can be accsessible by all sessions.
You even can do more. Using HttpSessionAttributeListener you can be notified every time the attribure is added, so if you want to create some kind of monitor application it may be more responsive.
In addition to Alex's response. The header attribute you are looking for is user-agent. Following is the code snippet of accessing browser type from request.
((javax.servlet.http.HttpServletRequest)pageContext.getRequest()).getHeader("user-agent")
In addition to Alex and Suken's responses, assuming the user has a single session you could store the deviceId's in a Map:
String userAgent = (see Suken's response)
String deviceId = request.getParameter("REQUEST_PARAMETER_DEVICE_ID");
Map<String, String> devices = request.getSession().getAttribute("SESSION_ATTRIBUTE_DEVICES");
if (devices == null) {
devices = new HashMap<String, String>();
request.getSession().setAttribute("SESSION_ATTRIBUTE_DEVICES", devices);
}
devices.put(userAgent, deviceId);
This makes sure the multiple devices remain visible and are not overwritten. You still need to expose them like Alex explained if you want to access them at application level.

Categories