Websocket: How To Push A Message To A Target User - java

I'm trying to implement a push notification in spring using websocket and with the use of sock.js.
These are the code snippets:
public class NotifyController {
#MessageMapping("/notifications")
#SendTo("/get/notifications")
public Greeting greeting(HelloMessage message) throws Exception {
new Greeting("Hello, " + message.getName() + "!");
}
}
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/get/notifications");
config.setApplicationDestinationPrefixes("/gssocket");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS();
}
}
This is the code in the front..
function connect() {
var notificationSocket = new SockJS('/notifications');
stompNotificationClient = Stomp.over(notificationSocket);
stompNotificationClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
stompNotificationClient.subscribe('/get/notifications', function(greeting){
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function sendNotification() {
var name = "test"
stompNotificationClient.send("/gssocket/notifications", {}, JSON.stringify({ 'name': name }));
}
I've already managed to make it work. But the question is, how can I push the message to certain target users. For example, there are 5 online users namely: user1, user2, user3, user4 and user5. I want the notification to be published to user1 and user2 only. Can you give me any idea on how to achieve this one? I'm thinking of either doing it in the backend or in the frontend. Or there is another way to achieve this using spring.
Somebody help me please.
Thank you.

You can check User destinations to target specific users with your messages, this is done using the SimpMessagingTemplate.convertAndSendToUser() call.
Note that to enable this functionality your users must be HTTP authenticated.

Related

Receiving Observed object changes on Leshan server

I am building a simple prototype based upon the leshan-server-demo included with the repo. I'm attempting to receive updates from objects that have been observed. Packet captures show that the updates are making their way to the server, but I'm receiving no notice of them.
The closest answer I found is from 2015 (How to retrieve updated content on an Observed resource in Leshan?) - but subsequent changes to the Leshan codebase have made the same technique unworkable.
I've tried using the ObservationService to add an ObservationListener, but that only seems to alert me when the Observe has been requested, not when the endpoint sends up changed values.
static private void attachListener(final LeshanServer server) {
System.out.println("Attaching Listener");
server.getObservationService().addListener(new ObservationListener() {
#Override
public void newObservation(Observation observation, Registration registration) {
System.out.println("New Observation");
}
#Override
public void cancelled(Observation observation) {
System.out.println("Observation cancellation");
}
#Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
System.out.println("Observation Response");
}
#Override
public void onError(Observation observation, Registration registration, Exception error) {
System.out.println("Observation Error");
}
});
}
How should I be listening for observed objects on the Leshan server?
You need to handle the onResponse:
From https://github.com/eclipse/leshan/blob/f315c66602b1061175f2441c019b862946d08a55/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/servlet/EventServlet.java#L133
#Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (LOG.isDebugEnabled()) {
LOG.debug("Received notification from [{}] containing value [{}]", observation.getPath(),
response.getContent().toString());
}
if (registration != null) {
String data = new StringBuilder("{\"ep\":\"").append(registration.getEndpoint()).append("\",\"res\":\"")
.append(observation.getPath().toString()).append("\",\"val\":")
.append(gson.toJson(response.getContent())).append("}").toString();
sendEvent(EVENT_NOTIFICATION, data, registration.getEndpoint());
}
}
The response.getContent() contains the new value. The data json that code builds will look like
{
"ep": "rpitest",
"res": "/3334/0",
"val": {
"id": 0,
"resources": [{
"id": 5704,
"value": 1.7929173707962036
}, {
"id": 5702,
"value": 0.9917597770690918
}, {
"id": 5703,
"value": 154.53704833984375
}]
}
}
If you are using Leshan-server-Demo and if you want to listen to client's notifed value changes from the browser, your frontend side can use eventsource (server sent event) to receive notifications from Leshan-Server Demo.
For example in my angular 7.0 app, I listen to COAP messages changes as below,
// Register Event Source
constructor() {
this.eventsource = new EventSource('server/event?ep=' + this.clientId);
}
// Add listener for COAP messages call back
ngOnInit() {
this.eventsource.addEventListener('COAPLOG', msg => this.coapLogCallback(msg), false);
console.error('Event Source', this.eventsource);
}
// modify coaplogs arrays
coapLogCallback(msg) {
var log = JSON.parse(msg.data);
if (100 < this.coaplogs.length) this.coaplogs.shift();
this.coaplogs.unshift(log);
}

Response of REST API with the logs generated during execution of the Request

I am writing some REST apis with swagger-ui . Now during the execution process of this apis I am performing some operation, Which I need to send as a response of the API. Consider the following response as an example:
{
"Database": [
"Table 1 created",
"data 1 inserted",
"Data 3 insertion failed"
],
"Kafka": [
"Topic 1 created",
"Topic 3 deleted",
"Topic 4 rebalanced"
]
}
So is there any framework for this, or I need to manually create the JSON object and send it as a response.
I suppose you are using Spring MVC?
First: create a class for api response.
public class Data {
private List<String> database = new ArrayList();
private List<String> kafka = new ArrayList();
public List<String> getDatabase() {
return database;
}
public void setDatabase(List<String> database) {
this.database = database;
}
public List<String> getKafka() {
return kafka;
}
public void setKafka(List<String> kafka) {
this.kafka = kafka;
}
}
Second: use #ResponseBody annotation against the controller's method. This will make spring understand that method return value should be bound to the web response body.
#ResponseBody
public Data apiMethod() {
return new Data();
}

Get current user with Spring #SubscribeMapping

I use #SubscribeMapping in a Spring Boot application which heavily relies on WebSockets for data exchange. The application is secured with Spring Security.
Client-side I use Stomp over WebSocket:
this.socket = new WebSocket(this.socketUrl);
this.stompClient = Stomp.over(this.socket);
this.stompClient.debug = null;
this.stompClient.connect({},
function(frame) {
this.$.chartLoader.generateRequest();
}.bind(this),
function(e) {
window.setTimeout(function() {
this.connectWs();
}.bind(this), 2500);
}.bind(this)
);
this.stompClient.subscribe('/topic/chart/' + chart.id,
function(message, headers) {
this.setChartData(chart, JSON.parse(message.body));
}.bind(this), {
"id" : "" + chart.id
}
);
Server-side, how can I get the currently logged user in the annotated methods ?
#SubscribeMapping("/chart/{id}")
public void subscribeMapping(#DestinationVariable("id") final short id) {
// Here I would need the current user...
messagingTemplate.convertAndSend("/topic/chart/" + id, chartService.getChartData(account, sensor, new Date(), new Date()));
}
I have tried SecurityContextHolder.getContext().getAuthentication(), but it returns null.
You can try to add a Principal object as a method parameter. This interface is extended by Authentication which has several implementations available (Spring will inject the good one according to your configuration).
public void subscribeMapping(#DestinationVariable("id") final short id, Principal principal) {
principal.getName();
}
This article may also help you.

smack presence listener in multi user chat

smack presence listener in multi user chat not getting called. Used Smack Api to login and then added roster.addRosterListener(mRoasterListener); but could not get any success to listen when presence of other user of the chat room changes. I tried following code to get the presence listener to work :
connection.login(loginUser, passwordUser);
MultiUserChatManager manager =
MultiUserChatManager.getInstanceFor(connection);
muc = manager.getMultiUserChat(roomID + "#" +context.getString(R.string.group_chat_id));
Log.d("Join User: ", "Already Created");
muc.join(Utilities.getUserPhoneNo(context));
muc.addMessageListener(mGroupMessageListener);
Roster roster = Roster.getInstanceFor(connection);//luna
roster.addRosterListener(mRoasterListener);//roasterListener
Log.d("Joined User Phone: ", " " + Utilities.getUserPhoneNo(context));
and this class to listen for presence change...
public class RoasterListener implements RosterListener{
public RoasterListener(Context context){
}
#Override
public void entriesAdded(Collection<String> collection) {
}
#Override
public void entriesUpdated(Collection<String> collection) {
}
#Override
public void entriesDeleted(Collection<String> collection) {
}
#Override
public void presenceChanged(Presence presence) {
System.out.println("Presence changed: " + presence.getFrom() + " " + presence);
}
}
I tried many links available by stackoverflow but could not get any success.
Please Help!
For Multi User Chat you don't have to use Roster, because it's normal to meet people you don't have in Roster.
To know who is in a muc, first ask for occupants:
muc.join(user,password);
List<String> occupantsAtJoinTime = muc.getOccupants();
for (String occupant : occupantsAtJoinTime)
{
System.out.println("occupant: "+occupant);
//actions
}
then, to keep Occupants list updated, register a DefaultParticipantStatusListener to your muc and define that Listner:
muc.addParticipantStatusListener(new CustomParticipantStatusListner());
definied as (there are many methods to implement if you need):
public class CustomParticipantStatusListner extends DefaultParticipantStatusListener
{
public void joined(String participant)
{
System.out.println(participant + "just joined MUC");
//actions (add occupantsRightNow)
}
public void left(String participant)
{
System.out.println(participant + " just left MUC");
//actions (remove occupantsRightNow)
}
}
All this with smack 4.1.7
It's about the Manage role modifications in Multi User Chat.
This example shows how to grant voice to a visitor and listen for the notification events:
// User1 creates a room
muc = new MultiUserChat(conn1, "myroom#conference.jabber.org");
muc.create("testbot");
// User1 (which is the room owner) configures the room as a moderated room
Form form = muc.getConfigurationForm();
Form answerForm = form.createAnswerForm();
answerForm.setAnswer("muc#roomconfig_moderatedroom", "1");
muc.sendConfigurationForm(answerForm);
// User2 joins the new room (as a visitor)
MultiUserChat muc2 = new MultiUserChat(conn2, "myroom#conference.jabber.org");
muc2.join("testbot2");
// User2 will listen for his own "voice" notification events
muc2.addUserStatusListener(new DefaultUserStatusListener() {
public void voiceGranted() {
super.voiceGranted();
...
}
public void voiceRevoked() {
super.voiceRevoked();
...
}
});
// User3 joins the new room (as a visitor)
MultiUserChat muc3 = new MultiUserChat(conn3, "myroom#conference.jabber.org");
muc3.join("testbot3");
// User3 will lister for other occupants "voice" notification events
muc3.addParticipantStatusListener(new DefaultParticipantStatusListener() {
public void voiceGranted(String participant) {
super.voiceGranted(participant);
...
}
public void voiceRevoked(String participant) {
super.voiceRevoked(participant);
...
}
});
// The room's owner grants voice to user2
muc.grantVoice("testbot2");
Details can be refered in http://web.mit.edu/svalente/lib/smack_3_0_4/documentation/extensions/muc.html .
Firstly, join a chat room:
public MultiUserChat joinMultiUserChat(String user, String roomsName,
String password) {
if (getConnection() == null)
return null;
try {
MultiUserChat muc = new MultiUserChat(getConnection(), roomsName
+ "#conference." + getConnection().getServiceName());
DiscussionHistory history = new DiscussionHistory();
history.setMaxChars(0);
// history.setSince(new Date());
muc.join(user, password, history,
SmackConfiguration.getPacketReplyTimeout());
Log.i("MultiUserChat", "Chat room【"+roomsName+"】joined........");
return muc;
} catch (XMPPException e) {
e.printStackTrace();
Log.i("MultiUserChat", "Chat room【"+roomsName+"】failed........");
return null;
}
}
Then, use MultiChatUser to send Message:
try {
multiUserChat.sendMessage(message);
} catch (XMPPException e) {
e.printStackTrace();
}
Add a Listener:
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
public class TaxiMultiListener implements PacketListener {
#Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
String body = message.getBody();
}
}
Finally, call the Listener using MultiUserChat:
multiUserChat.addMessageListener(new TaxiMultiListener());

TimeoutException on telegram java client

i used the java telegram api to communicate with telegram core api in windows intellij idea
https://github.com/ex3ndr/telegram-api
But the app is facing Timeout error in line TLConfig config = api.doRpcCall(new TLRequestHelpGetConfig());Full source code:
AppInfo appinfo=new AppInfo(45687, "Myapp", "154", "587","en");
TLRequestAuthCheckPhone checkRequest = new TLRequestAuthCheckPhone("96521452365");
MyApiStorage state=new MyApiStorage();
TelegramApi api = new TelegramApi(state, appinfo, new ApiCallback()
{
public void onApiDies(TelegramApi api) {
// When auth key or user authorization dies
}
#Override
public void onUpdatesInvalidated(TelegramApi api) {
System.out.print("############################### onUpdatesInvalidated");
// When api engine expects that update sequence might be broken
}
#Override
public void onAuthCancelled(TelegramApi ta) {
System.out.print("############################### onAuthCancelled");
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void onUpdate(TLAbsUpdates updates) {
System.out.print("############################### onUpdate");
System.out.println("user Id ::::"+((TLUpdateShortMessage) updates).getFromId());
}
});
api.switchToDc(1);
TLConfig config = api.doRpcCall(new TLRequestHelpGetConfig());
System.out.print("############################### config" + config.getTestMode());
state.updateSettings(config);
api.doRpcCall(checkRequest, new RpcCallbackEx<TLCheckedPhone>() {
public void onConfirmed() {
System.out.print("############################### onConfirmed");
}
public void onResult(TLCheckedPhone result) {
boolean invited = result.getPhoneInvited();
boolean registered = result.getPhoneRegistered();
System.out.print("############################### onResult" + registered);
// TODO process response further
}
public void onError(int errorCode, String message) {
System.out.print("############################### onError" + message);
}
});
can someone help me
Your timeout might happen for several reasons:
1. You are using
api.doRpcCall(new TLRequestHelpGetConfig());
In the TelegramApi class this translates into
return this.doRpcCall(method, timeout, 0);
0 there stands for DC. If your DC is different you will timeout
2. There were suggestions in other places to use doRpcCallSide instead and it worked for some and not for others. The reason is it translates into
return this.doRpcCall(method, 15000, this.primaryDc, true);
where true stands authRequired.
3. If you want to do this without authorization then use api.doRpcCallNonAuth

Categories