spring websocket notification - spring

Ok here i'm , i'm right now following the guides on spring site but i'm having problem on how to deliver a notification to only one user using WebSocket, i'm following this guide https://spring.io/guides/gs/messaging-stomp-websocket/ .
What I want is: i have 2 users, both of them subscribe to process1... User1 need to let the server process his pass...now i want that the server will deliver the notification only to User1...
#Controller
public class ProcessController {
#MessageMapping("/ProcessOwner/approve/{pass}")
#SendTo("")
public String notifica(#DestinationVariable String pass)throws Exception{
return "ok"+pass;
}
}
Now what should i write in the #SendTo field to deliver the answer only to user1 ? if ill write /Process/process1 both user1 and user2 will receive the message....

You ca use the sufix. It's send to every unique client.
When you subscribe in the js code:
client.connect('guest', 'guest', function(frame) {
var suffix = frame.headers['queue-suffix'];
client.subscribe("/queue/error" + suffix, function(msg) {
// handle error
});
client.subscribe("/queue/position-updates" + suffix, function(msg) {
// handle position update
});
});
On the server side you can use #ReplyToUser or the message template
String user = "fabrice";
String queue = "/queue/position-updates";
this.messagingTemplate.convertAndSendToUser(user, queue, position);
See more here: http://assets.spring.io/wp/WebSocketBlogPost.html (section: Sending Messages To a Single User)

Related

Masstransit: GetSendEndpoint

I have a producer, which send more than 1000 messages in a minute to a specific endpoint. I’m using Microsoft DI and I’ve configured the send Endpoint as described here https://masstransit-project.com/usage/producers.html#send .
// Masstransit setup
serviceCollection.AddMassTransit(mt =>
{
mt.UsingAzureServiceBus((ctx, cfg) =>
{
cfg.Host(massTransitSettings.TestServiceBusConnectionString);
cfg.ReceiveEndpoint("mytestmessage", e =>
{
e.MaxDeliveryCount = 3; //How many times the transport will redeliver the message on negative acknowledgment
});
});
});
serviceCollection.AddTransient<ITestMessageProducer, TestMessageProducer>();
// Producer setup
public class TestMessageProducer : ITestMessageProducer
{
private readonly ISendEndpointProvider _testEndpoint;
public TestMessageProducer(ISendEndpointProvider testEndpoint)
{
_testEndpoint = testEndpoint;
}
public async Task SendTestMessage(ITestMessage testmessage)
{
var endpoint = await _testEndpoint.GetSendEndpoint(new Uri("queue:mytestmessage"));
await endpoint.Send(testmessage);
}
}
Query:
The SendTestMessage function has been called very frequently as mention above. Will it be ok to call “GetSendEndpoint” everytime? I have read somewhere that GetSendEndpoint creates a new instance of ISendEndpoint everytime.
Will the MaxDeliveryCount still be worked on my sendendpoint?
Thank you.
Send endpoints are cached by address, only a single instance will be created.
MaxDeliveryCount is a receive endpoint concern, but you should not configure a receive endpoint without consumers as all messages will be moved to the _skipped queue.

nestjs event-based messaging with 1 producer and 2 consumers

With nestjs microservices, you can send messages and receive the result using request/response-based approach. That is implemented with a combination of #MessagePattern and client.send('my_pattern', myData). For an example see the nest docs: https://docs.nestjs.com/microservices/basics#request-response and https://docs.nestjs.com/microservices/basics#sending-messages.
How do I receive the result in the event-based approach?
Suppose you have a user microservice and an auth microservice. Whenever a user is created you want an auth subject to be created as well (saving the username and a hash of the password, so that the user may login with an api request to the auth microservice instead of the user service).
auth/auth.controller.ts
#EventPattern('EVT_USER_CREATED')
public async handleUserCreated(data: any): Promise<AuthSubject> {
if (!data.username || !data.password) {
throw new RpcException('Auth subject must supply username and password');
}
const newSubject: CreateAuthSubject = {
username: data.username,
password: data.password,
email: data.email ?? '',
};
const sub = await this.subjectService.create(subject);
return sub;
}
user/user.controller.ts
#Post('')
#ApiBody({ type: CreateUser })
#ApiCreatedResponse({ type: User })
public async create(#Body() user: CreateUser): Promise<User> {
const newUser = await this.userService.create(user);
this.userQueue
.emit<any, CreateUser>('EVT_USER_CREATED', user)
.subscribe((res) => {
console.log(res); // undefined
});
return newUser;
}
To verify that there is no error in my setup, I changed #EventPattern to #MessagePattern and this.userQueue.emit<... to this.userQueue.send<.... It worked, i.e. res was a valid auth subject with username and password as expected. However, with the event-based approach outlined in this question res is always undefined (whether or not the auth controllers handleUserCreated returns or throws).
Ultimately I would like to achieve the following: If another microservice needs to process 'EVT_USER_CREATED' events, I just add a #EventPattern('EVT_USER_CREATED') public async handleUserCreated method to its controller. The observable this.userQueue.emit<any, CreateUser>('EVT_USER_CREATED', user) would then receive both results: Once for each of the microservices consuming the user created event.
So suppose I add a third microservice: the customer microservice that is responsible for saving payment information, the history of orders, etc. Like the auth service it subscribes to 'EVT_USER_CREATED'.
customer/customer.controller.ts
#EventPattern('EVT_USER_CREATED')
public async handleUserCreated(data: any): Promise<AuthSubject> {
const customer = await this.customerService.create(data);
return customer ;
}
Now with the above setup the microservices auth and customer will alternate in receiving the events: If the user microservices emits the creation of a user, only the auth service will react to it and create an auth subject from hat user. No customer will be created for that user. For the next user that is created in the user microservice, only a customer will be created but not an auth subject. The third created user will again be consumed by the auth microservice but not by the customer microservice. And so on.
-- auth microservice
/
user microservice --- message broker ---
\
-- customer microservice
To summarize: How do I achive the messaging architecture shown in the diagram, such that I only need one emit(...) call in the user.controller.ts and, such that I reveive both responses in the subscrption to the emit(...) call?
It may be a bit late but I leave my contribution for future users.
For this type of architecture strongly based on events, it is advisable to use a messaging broker such as Kafka, with which different services can be subscribed to different topics, and even different services can listen to the same topic, in this way you can react in different ways to the same event.
Kafka also offers many advantages that will be useful when you want to scale your microservices, and as an extra, it has support in Nest.
https://docs.nestjs.com/microservices/kafka
What you should keep in mind is that when messaging systems are used, communication is usually completely asynchronous, this is an important detail since, following the case you present, not only is it enough for you to send a message through kafka to another microservice to validate the credentials of a user, but it is necessary to return a response to the client. For this case, the tools offered by nest are limited since using the #MessagePattern decorator, we can receive messages from kafka in our controller, but we cannot wait for a response (of the same topic or another) with the confirmation of success to respond to the Customer request. In this case, it would be convenient for you to create your own transporter or your own kafka client with Kafkajs (https://kafka.js.org/) so that if necessary, you can keep the user's request until you receive a confirmation for another topic.
A solution that I have seen in certain forums is to save an object in memory (key / value) with the "Response" object of a request associated with the id of a user, in this way you can send a message by kafka or any other broker with a specific action, and receive the confirmation by another controller, retrieve the user's "Request" object and then, send the response (Request.send (...)). It is not the ideal solution but it works for some cases.
For example:
#Controller('users')
class MyController {
constructor(private kafkaClient: KafkaClient) {}
private users: new Map<string, Request>() // Or private users: any = {}
onModuleInit() {
// By default, NestJs create topic "users-topic.reply"
this.kafkaClient.subscribeToResponseOf('users-topic')
}
#Post('auth')
authUser(#Req req: Request, #Res res: Response): void {
const userData = req.body;
const { id } = userData;
// Save request object in private variable;
this.users.set(id, res); // or this.users[id] = res;
// Send kafka message to validate user data
this.kafkaClient.emit('users-topic')
}
#MessagePattern('users-topic.reply')
authUser(#Payload() data: any): any {
if (data.message === 'success') {
const res:Request = this.users.get(data.id); // or this.users[data.id];
// Clean users object
this.users.remove(data.id); // or this.users[data.id] = null;
// Response client request
res.send('Authentication successfully')
}
}
}

Spring websocket with stomp: private message to multiple instances of same user

I am using Springwebsockets with the STOMP protocol over Websockets, and i am using the in memory borker. I want to send messages to specific users.
On the client side i subscribe to a private topic:
stompClient.subscribe('/user/topic/private', function(greeting){
});
And i send the message like this:
stompClient.send("/user/"+ user +"/topic/private", {}, message);
This all works fine and i don't have to do anything on the server. But when i log in twice with the same user (eg different browsers, one desktop and one mobile), it doesn't work like i expected. I would expect that the private message ends up in both the browser. However it only seems to end up in the current browser.
Is there any better way to do this kind of private messaging? Or did i misunderstand how the private channel works?
You can try this:
#SendToUser(value = "your destination",broadcast = true)
public String handleException(Message message) {
//your logic goes here
return message; // this will be sent to user
}

Spring Stomp #SubscribeMapping for User Destination

I am trying to get notified when an user subscribes to a stomp user destination using #SubscribeMapping annotation. My ideia is to send some initialization data to when it joins.
Although i am not being able to get this working:
Javascript:
stompClient.subscribe('/user/monitor', function(msg) {
console.log(msg);
});
Java side:
#SubscribeMapping("/monitor-user{clientId}")
public void init(#DestinationVariable("clientId") String clientId) {
messagingTemplate.convertAndSend("/user/" + clientId + "/monitor", getOps());
}
I tried many mapping combinations such as "/user/monitor-{clientId}", "/user/monitor" (Removing clientId), no success at all.
What is the expected mapping value so this get called?
Thank you!
Since the client subscribes to the "/user/monitor" queue, the server #SubscribeMapping annotation must be to "/user/monitor" and not to "/user/monitor{something}".
The server can understand what client is depending on your authentication scheme. If you use a sockjs websocket you can use HTTP authentication, and hence leverage Spring Security, and add a "Principal" parameter to your function that will hold user information:
#SubscribeMapping("/monitor")
public void init(Principal p) {
String user = p.getName();
messagingTemplate.convertAndSendToUser(user, "/monitor", getOps());
}
If you don't use http authentication you may send the server the user info, for example adding a custom STOMP header that can be accessed from the server using a SimpMessageHeaderAccessor:
#SubscribeMapping("/monitor")
public void init(SimpMessageHeaderAccessor accessor) {
String user = accessor.getFirstNativeHeader("userid");
messagingTemplate.convertAndSendToUser(user, "/monitor", getOps());
}
Another way could be to subscribe to a different queue name which contains the user information (and this maybe was your proposal). The client must use a code similar to:
stompClient.subscribe('/topic/byUser/'+userId, function(msg) {
console.log(msg);
});
so that the server can access it this way:
#SubscribeMapping("/byUser/{userId}")
public void init(#DestinationVariable String userId) {
messagingTemplate.convertAndSendToUser(userId, "/monitor", getOps());
}
but in this case keep in mind that that queue is public and hence other clients can access their messages if they knows other client names.

Contact Form EMail not working

I have created a contact page and contact model that has From Subject and Message as string values. Now when I try to send email from my development environment with code below, it won't work. I browsed around a bit looking for a solution but several things are unclear to me since I haven't dealt with this too often.
This is the method I use for sending e-mail. Commented part is also one of the attempts.
The error I get is: System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond
I realize that this probably has something to do with me being on dev.env., does it? What am I doing wrong?
public class Email
{
public void Send(Contact contact)
{
MailMessage mail = new MailMessage(
contact.From,
ConfigurationManager.AppSettings["ContactEmail"],
contact.Subject,
contact.Message);
//new SmtpClient().Send(mail);
WebMail.Send(ConfigurationManager.AppSettings["ContactEmail"], contact.Subject, contact.Message, null, null, null, false, null);
}
}
Can you send mail like this, instead?
internal static void SendEmail(MailAddress fromAddress, MailAddress toAddress, string subject, string body)
{
var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
};
var client = new SmtpClient("smtpServerName");
client.Send(message);
}
Ref. asp.net mvc framework, automatically send e-mail
I think you need to check port number in you config file.

Resources