I am trying to send a message using stomp from server to a client. I know that using sock.js and stomp on client side I can send a message from one user to another, without much server-side interaction, simply by using a #SendTo annotation in a controller method. However, the message that I want the user to receive is generated on the server (actually, I'm sending a whole object, but for simplicity's sake, let's just say I'm trying to send a String). Specifically, this deals with friend request acceptance, and when one user accepts a friend request, the one who sent the request should receive a message that his request was accepted. So, after a simple ajax call to a rest controller method for accepting a request, the method should also send the message to the other user. Here's the code:
#RestController
#RequestMapping("/rest/user")
public class UserController{
#Autowired
SimpMessagingTemplate simp;
#RequestMapping(value="/acceptFriendRequest/{id}", method=RequestMethod.GET, produces = "application/json")
public boolean acceptFriendRequest(#PathVariable("id") int id){
UserDTO user = getUser(); // gets logged in user
if (user == null)
return false;
... // Accept friend request, write in database, etc.
String username = ... // gets the username from a service, works fine
simp.convertAndSendToUser(username, "/project_sjs/notify/acceptNotification", "Some processed text!");
return true;
}
}
And here's the web socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/sendNotification").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/notify");
config.setApplicationDestinationPrefixes("/project_sjs");
}
}
And this is the javascript function:
function setupWebsockets(){
var socketClient = new SockJS("/project_sjs/sendNotification");
stompClient = Stomp.over(socketClient);
stompClient.connect({}, function(frame){
stompClient.subscribe("/project_sjs/notify/acceptNotification", function(retVal){
console.log(retVal);
});
});
}
When a user accepts the friend request, everything is written fine in the database. I can even see that the other user is my friend now when I refresh the page. However, the other user never receives the message that his request was accepted.
Is there anything I'm doing wrong? Any help would be greatly appreciated. Thanks!
I solved this problem with a different approach. Instead of subscribing all users to the same endpoint "/project_sjs/notify/acceptNotification", and then differentiating them by username, I ended up subscribing each user to a different endpoint, for example "/project_sjs/notify/acceptNotification/John123". This way, everyone with the username John123 (which is only one person, because usernames are unique) will get the notification. And it worked well enough.
Related
So, I'm currently working on session management and found out that one of the ways of doing so is by using cookies. I'm using spring boot as my backend tool.
So,
#PostMapping("/addUser")
public User addUser(#RequestBody String name, HttpServletRequest request, HttpServletResponse response) {
HttpSession getSession = (HttpSession) request.getAttribute("session");
Cookie cookie = new Cookie("sessionId", getSession.getId());
response.addCookie(cookie);
sessionService.addSession(getSession);
String session = (String) getSession.getId();
System.out.println("Session id is " + session);
User newUser = new User(name, session);
userService.addUser(newUser);
return newUser;
}
This is the code that's responsible for creating cookies. Where, what happen is user will enter the username on a form and click on submit and then it will be post mapped on this method.
So, I tried checking if I'm getting cookies from frontend or not using this method
#PostMapping("/noOfUsers")
public int userCount(#RequestBody String sessionId, HttpServletRequest request, HttpServletResponse response) {
System.out.println(request.getSession().getId());
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("sessionId")) {
System.out.println(cookie.getValue());
}
}
}
return this.userService.noOfUser();
}
When I used postman, I did get the cookie value printed but don't know how in angular I'm suppose to accept the cookie send by backend and in the end send back the cookie on each request made using frontend.
I am using react for the frontend and springboot for the backend. I am not able to retrieve the data that I sent using axios in the backend. The first code below is the frontend where I make post and send 3 objects that I want to use in the backend. And the second code segment is the backend where I have post mapping but really confused on how to get those 3 objects that I sent from frontend. Also the user is where I have the getter and setters for name, message, and email so I want to set the data from frontend into those variables in User. I am somewhat new to springboot but I have some experience in connecting database to springboot but in this case I dont need to use database to store anything. The overall goal is for me to achieve a working contact form where users can submit comments/complaints about a webpage and it will direct those emails to me.
const info = {
name: "Test"
message: "This is comment for test",
email: "test#test.com
};
axios.post("http://localhost:8080/postgressApp/signup-success", info)
.then(response => {
if(response.data != null) {
this.setState({show:true});
setTimeout(() => this.setState({show:false}), 3000);
window.location.reload();
} else {
this.setState({show:false});
}
});
#RestController
#RequestMapping("/postgressApp")
#CrossOrigin(origins="http://localhost:3000")
public class RegistrationController {
private Logger logger = LoggerFactory.getLogger(RegistrationController.class);
#Autowired
private NotificationService notificationService;
#PostMapping("/signup-success")
public String signupSuccess(){
// create user
User user = new User();
// send a notification
try {
notificationService.sendNotificaitoin(user);
}catch( MailException e ){
// catch error
logger.info("Error Sending Email: " + e.getMessage());
}
return "Thank you for registering with us.";
}
}
Change your method signature like this:
...
#PostMapping("/signup-success")
public String signupSuccess(#RequestBody User user) {
...
}
#RequestBodyannotation tells Spring to bind the incoming http request's body to your type. It will check your request parameter keys and values, check the type you provided after the annotation, try to match parameter keys with the fields in your User class and then copy the values from request to your User instance.
Summary:
IDE: Eclipse
Server: Java
Client: Html/Javascript
After sending data via ajax/jquery to a servlet and using JadeGateway to send those information to an agent, resulting data should go back to html/javascript.
I need a way to connect either a servlet or an agent with an existing and running html file (ofc without reloading the page).
Background:
I want to create a agent based game using babylon.js and jade/pug (let's call it jade).
During a game, information about game states should go to an agent, so that he could figure out what to do (e.g. create more units or attack). After this, the agent needs to send the information to the game, to call the appropriate function.
What I did so far:
I connected the game (html/javascript) with a servlet using ajax. There I used JadeGateway to send an ACLMessage to an agent.
I also used websockets to connect client/server but this method is too limited for what i need i think.
I now need a tool/method or tips to send information from an agent or the servlet. Both is possible, because the agent can send information back to the jadegateway agent/servlet.
I know how to use ajax to call a servlet from html and right back, but now the information is tranfered to some other classes, so that didn't work anymore.
In the babylon.js scene.registerBeforeRender function I do this:
$.ajax({
url: "AgentListenerServlet",
type: "POST",
data: {playerString: convertPlayerToJson(player1)},
success: function(data) {
alert(data.command)
},
error: function() {
alert("Listener Failed")
}
});
Sending message to an agent via jadegateway:
JadeGateway.execute(new CyclicBehaviour() {
public void action() {
final ACLMessage msgSend = new ACLMessage(ACLMessage.INFORM);
msgSend.setContent(message);
msgSend.addReceiver(new AID("First", AID.ISLOCALNAME));
myAgent.send(msgSend);
final ACLMessage msgReceive = myAgent.receive();
if(msgReceive != null && msgReceive.getContent() != null){
//used this as immediate answer for ajax success function, but maybe it can also be used to send the returning agent message?
Map <String, Object> map = new HashMap<String, Object>();
map.put("command", "information");
write(response, map);
} else {
block();
}
}
});
private void write(HttpServletResponse response, Map<String, Object> map) throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(new Gson().toJson(map));
}
agent code (simplified):
public void action() {
final ACLMessage msgSend = new ACLMessage(ACLMessage.INFORM); //sending message
final ACLMessage msgReceive = receive(); //receiving message
//Here i get information out of sent json objects from jadegateway
//Maybe here send handled information back to html?
} else {
block();
}
}
Maybe showing this codes is not necessary, because they are working and the problem is not there, but the template for asking a question requested these.
Expected and results:
So the problem is not, that it isn't working, but that I need to know how to continue.
I don't know what I expect, because i don't know the possibilities.
Maybe, what would help me, is to send a simple string from the agent or servlet that i can call with the alert function in the game.
Or maybe i should ask this: is it possible to get an answer in ajax (success function) after the data was sent to the servlet and passed to an agent?
Sorry for the huge spam. Hope I made my problem clear.
No need to reply, websockets is the solution after all.
I just figured out, that you can push data from server to client without a client request with
session.getBasicRemote().sendText(msgReceive.getContent());
The session is automatically generated and you can reveice it with websockets onopen method:
#OnOpen
public void onOpen(Session session){
this.session = session;
}
I'm going on a long flight tomorrow and I'd like to be able to keep testing my cloud endpoints REST API while offline. The problem is that the User object is integral to most of my methods, and I need an internet connection to create valid OAuth tokens to call them from the client side (JavaScript).
On the Dev server though, no matter what account you log in on, the user is always the same (with email example#example.com). But if you feed it bogus tokens, it throws an OAuthRequestException.
Is there any way I can generate valid test tokens offline for the dev server or a way to access the User object without providing tokens at all?
Here's an example of a method I'd like to test while offline:
#ApiMethod(name = "hylyts.get")
public Hylyt getHylyt(#Named("url") String url, #Named("id") long id, User user)
throws OAuthRequestException, UnauthorizedException {
return ofy().load().type(Hylyt.class).parent(util.getArticleKey(url, user)).id(id).now();
}
There's a little documented way to inject a custom Authenticator class in Cloud Endpoints. This allows you to change the way the User is detected.
Here's how it works :
#Api(name = "myapi", version = "v1", authenticators = {MyDummyAuthenticator.class})
public class MyAPI {
#ApiMethod(name = "hylyts.get")
public Hylyt getHylyt(#Named("url") String url, #Named("id") long id, User user)
throws OAuthRequestException, UnauthorizedException {
return ofy().load().type(Hylyt.class).parent(util.getArticleKey(url, user)).id(id).now();
}
}
And here's what your Authenticator implementation could look like :
public class MyDummyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest httpServletRequest) {
return new User("mytestuser#domain.com");
}
}
You can of course make it more complicated. Since you have access to the HttpServletRequest you can get the user's email from a HTTP header or something like it.
Note that with an Authenticator you have access to the session in the local server but not in production. In production, httpServletRequest.getSession() will return null. THere's a trick to still fetch the session from the datastore, which I explain here.
Then there's the question of how to keep both the normal authentication solution and your DummyAuthenticator implementation. I think you can chain authenticators, but I'm not sure how it works. In the worst case, you can just swap the Authenticator implementation during your flights.
I am new to WebSockets.
I have already made a simple server-client chat in WebSockets.
And now I am trying to make client-server-client chat application.
I have a question that in java server how can we send a message to particular WebSocket connection.
If user-A want to send a message to User-B.
Then how can I manage that User-B is using this or that connection or send a message to that particular connection?
I am searching too much for this on google but could not find anything good.
You have to design an architecture for that.
When a client establishes a connection with the server (opens the WebSocket), the server has to keep the connection somewhere (howsoever you're identifying a specific connection with the Java backend you're using), in a data structure that will depend on what you're trying to do. A good identifier would be an ID the user provides (like a nickname that's not already picked by another peer connected to the same server). Otherwise, simply use the socket object as a unique identifier and, when listing other users on the frontend, associate them with their unique identifier so that a client can send a message to a specific peer.
A HashMap would be a good choice for a data structure if a client is going to chat with another specific client, as you can map the unique ID of a client to the socket and find an entry with in O(1) in a hash table.
If you want to broadcast a message from a client to all other clients, although a HashMap would also work pretty well (with something like HashMap.values()), you may use a simple List, sending the incoming message to all connected clients except the original sender.
Of course, you also want to remove a client from the data structure when you lose connection with it, which is easy using a WebSocket (the Java framework you are using should call you back when a socket closes).
Here's an (almost complete) example using a Jetty 9 WebSocket (and JDK 7):
package so.example;
import java.io.IOException;
import java.util.HashMap;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
#WebSocket
public class MyWebSocket {
private final static HashMap<String, MyWebSocket> sockets = new HashMap<>();
private Session session;
private String myUniqueId;
private String getMyUniqueId() {
// unique ID from this class' hash code
return Integer.toHexString(this.hashCode());
}
#OnWebSocketConnect
public void onConnect(Session session) {
// save session so we can send
this.session = session;
// this unique ID
this.myUniqueId = this.getMyUniqueId();
// map this unique ID to this connection
MyWebSocket.sockets.put(this.myUniqueId, this);
// send its unique ID to the client (JSON)
this.sendClient(String.format("{\"msg\": \"uniqueId\", \"uniqueId\": \"%s\"}",
this.myUniqueId));
// broadcast this new connection (with its unique ID) to all other connected clients
for (MyWebSocket dstSocket : MyWebSocket.sockets.values()) {
if (dstSocket == this) {
// skip me
continue;
}
dstSocket.sendClient(String.format("{\"msg\": \"newClient\", \"newClientId\": \"%s\"}",
this.myUniqueId));
}
}
#OnWebSocketMessage
public void onMsg(String msg) {
/*
* process message here with whatever JSON library or protocol you like
* to get the destination unique ID from the client and the actual message
* to be sent (not shown). also, make sure to escape the message string
* for further JSON inclusion.
*/
String destUniqueId = ...;
String escapedMessage = ...;
// is the destination client connected?
if (!MyWebSocket.sockets.containsKey(destUniqueId)) {
this.sendError(String.format("destination client %s does not exist", destUniqueId));
return;
}
// send message to destination client
this.sendClient(String.format("{\"msg\": \"message\", \"destId\": \"%s\", \"message\": \"%s\"}",
destUniqueId, escapedMessage));
}
#OnWebSocketClose
public void onClose(Session session, int statusCode, String reason) {
if (MyWebSocket.sockets.containsKey(this.myUniqueId)) {
// remove connection
MyWebSocket.sockets.remove(this.myUniqueId);
// broadcast this lost connection to all other connected clients
for (MyWebSocket dstSocket : MyWebSocket.sockets.values()) {
if (dstSocket == this) {
// skip me
continue;
}
dstSocket.sendClient(String.format("{\"msg\": \"lostClient\", \"lostClientId\": \"%s\"}",
this.myUniqueId));
}
}
}
private void sendClient(String str) {
try {
this.session.getRemote().sendString(str);
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendError(String err) {
this.sendClient(String.format("{\"msg\": \"error\", \"error\": \"%s\"}", err));
}
}
The code is self explanatory. About JSON formatting and parsing, Jetty has some interesting utilities within package org.eclipse.jetty.util.ajax.
Also note that if your WebSocket server framework is not thread-safe, you will need to synchronize the data structure to make sure there's no data corruption (here MyWebSocket.sockets).