In tomcat-8 examples I have seen a example on chat using HTML5 web sockets.
The code is shown below
public class ChatAnnotation {
private static final Log log = LogFactory.getLog(ChatAnnotation.class);
private static final String GUEST_PREFIX = "Guest";
private static final AtomicInteger connectionIds = new AtomicInteger(0);
private static final Set<ChatAnnotation> connections =
new CopyOnWriteArraySet<>();
private final String nickname;
private Session session;
public ChatAnnotation() {
nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
}
#OnOpen
public void start(Session session) {
this.session = session;
connections.add(this);
String message = String.format("* %s %s", nickname, "has joined.");
broadcast(message);
}
#OnClose
public void end() {
connections.remove(this);
String message = String.format("* %s %s",
nickname, "has disconnected.");
broadcast(message);
}
#OnMessage
public void incoming(String message) {
// Never trust the client
String filteredMessage = String.format("%s: %s",
nickname, HTMLFilter.filter(message.toString()));
broadcast(filteredMessage);
}
private static void broadcast(String msg) {
for (ChatAnnotation client : connections) {
try {
synchronized (client) {
client.session.getBasicRemote().sendText(msg);
}
} catch (IOException e) {
log.debug("Chat Error: Failed to send message to client", e);
connections.remove(client);
try {
client.session.close();
} catch (IOException e1) {
// Ignore
}
String message = String.format("* %s %s",
client.nickname, "has been disconnected.");
broadcast(message);
}
}
}
}
This code send a message to all the clients who connected to server .
But I want to send message to only "Guest1".
I think for loop has to be change.
How to send message to only "Guest1".
Convert connections from set into a map:
ConcurrentHashMap< String, ChatAnnotation> connections = new ConcurrentHashMap<>();
Keep user or whatever identifier you have to identify user as key in the map. They get the connection object from map using user key in broadcast method and send message only to that user, instead of iterating over all connection objects.
Related
I am trying to create a websocket using sparkjava framework. Below is the code for create a websocket
public final class MainWS {
static Map<Session, String> USER_SESSION_MAP = new ConcurrentHashMap<>();
static int nextUserNumber = 1;
public static void main(String[] args) {
port(8090);
webSocket("/echo", ChatWebSocketHandler.class);
init();
}
public static void broadcastMessage(String sender, String message) {
USER_SESSION_MAP.keySet().stream().filter(Session::isOpen).forEach(session -> {
try {
session.getRemote().sendString(String.valueOf(new JSONObject().put("userMessage", "message to pass")
.put("userlist", USER_SESSION_MAP.values())));
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
Now the CharWebSocketHandler code is as below:
#WebSocket
public final class ChatWebSocketHandler {
private String sender, msg;
#OnWebSocketConnect
private void onConnect(Session user) throws Exception {
String username = "User" + MainWS.nextUserNumber++;
MainWS.USER_SESSION_MAP.put(user, username);
MainWS.broadcastMessage(sender = "Server", msg = (username + " joined the Main"));
}
#OnWebSocketClose
private void onClose(Session user, int statusCode, String reason) {
String username = MainWS.USER_SESSION_MAP.get(user);
MainWS.USER_SESSION_MAP.remove(user);
MainWS.broadcastMessage(sender = "Server", msg = (username + " left the Main"));
}
#OnWebSocketMessage
private void onMessage(Session user, String message) {
MainWS.broadcastMessage(sender = MainWS.USER_SESSION_MAP.get(user), msg = message);
}
}
After running my MainWS program i am using rxjs for getting websocket connection via Angular
The code is as below:
export class WebsocketService {
socket: WebSocketSubject<WSMessageService>
constructor() {
this.socket = new WebSocketSubject("ws://localhost:8090/echo");
this.socket.subscribe(
msg => {
console.log(msg)
},
err => {
console.log(err)
},
() => {
console.log('complete')
}
);
}
public sendMessage(message: WSMessageService): void {
this.socket.next(message)
}
}
Now when i try to run my code i am getting error as below:
Firefox can’t establish a connection to the server at ws://localhost:8090/echo
error { target: WebSocket, isTrusted: true, srcElement: WebSocket, currentTarget: WebSocket, eventPhase: 2, bubbles: false, cancelable: false, returnValue: true, defaultPrevented: false, composed: false, … }
Not at all sure where exactly i am doing wrong. If anybody have any idea please let me know.
The issue is i made all private methods in ChatWebSocketHandler class. After making all public method i am able to connect and get websocket object.
I'm trying to implement a fake broker (actually it is an mqtt publisher client in an mqtt subscriber's callback). There are 3 separated publisher clients which are publishing random numbers between 0 and 1. This fake broker just summarizes this random numbers, and publishes away to an other topic. (Maybe not in the right way, but for now it is ok) This solution is working but after a few incoming messages this broker stops to work. I Tried to debug it, but I found only ClassNotFound Exceptions... Here is my FakeBroker and it's Callback implementation.
public class FakeBroker implements Runnable{
public static final String BROKER_URL = "";
public static final String TOPIC_FAKE_A = "";
public static final String TOPIC_FAKE_B = "";
public static final String TOPIC_FAKE_C = "";
public static final String USER_NAME = "";
public static final char[] USER_PSW = "".toCharArray();
private MqttClient client;
private MqttConnectOptions options;
private SubscriberCallback callback;
public FakeBroker() {
options = new MqttConnectOptions();
options.setUserName(USER_NAME);
options.setPassword(USER_PSW);
options.setCleanSession(false);
callback = new SubscriberCallback();
try {
client = new MqttClient(BROKER_URL, MqttClient.generateClientId()+"-sub");
client.setCallback(callback);
} catch (MqttException e) {
e.printStackTrace();
System.exit(1);
}
}
public void start() {
try {
client.connect(options);
System.out.println("Fake Broker are connected to the cloud.");
client.subscribe(TOPIC_FAKE_A);
client.subscribe(TOPIC_FAKE_B);
client.subscribe(TOPIC_FAKE_C);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void run() {
start();
}
}
And here is it's Callback
public class SubscriberCallback implements MqttCallback {
public static final String BROKER_URL = "";
public static final String TOPIC_FAKE_BROKER = "";
public static final String USER_NAME = "";
public static final char[] USER_PSW = "".toCharArray();
private MqttClient client;
private MqttConnectOptions options;
private int counter = 1;
private int result = 0;
public SubscriberCallback() {
try {
client = new MqttClient(BROKER_URL, "4-pub");
options = new MqttConnectOptions();
options.setPassword(USER_PSW);
options.setUserName(USER_NAME);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void connectionLost(Throwable throwable) {
}
#Override
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
System.out.println("Message Arrived. Topic " + topic + " message: " +mqttMessage + " ---- Message Counter: " + counter);
int number = Integer.parseInt(mqttMessage.toString());
result += number;
if (counter%3 == 0) {
publishAway(new MqttMessage(Integer.toString(result).getBytes()));
result = 0;
}
incrementCounter();
}
private void publishAway(MqttMessage mqttMessage) throws MqttException {
client.connect(options);
final MqttTopic topicFakeBroker = client.getTopic(TOPIC_FAKE_BROKER);
topicFakeBroker.publish(mqttMessage);
client.disconnect();
System.out.println("Fake broker got the message " + mqttMessage + " and published away to" + topicFakeBroker.getName());
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
}
private void incrementCounter() {
counter++;
}
}
Of course I use valid BROKER_URL and TOPICS but these informations are confidential. Thanks for Your answers! :)
I'm new to mqtt. Getting started I tried publishing and subscribing topics to mosquitto broker. I was able to publish messages. But my subscriber is not listening to the topic, it will start and stop without waiting/polling for messages.
Here is the subscriber code,
public class MqttSubscriber implements MqttCallback {
private static final String TOPIC = "iot/endpoint";
public static void main(String[] args) {
new MqttSubscriber().listen();
}
public void listen() {
MqttClient client = null;
try {
client = MqttClientGenerator.generateSubscriberClient();
client.connect();
System.out.println("Fetching messages...");
client.subscribe(TOPIC);
client.setCallback(this);
client.disconnect();
} catch (MqttException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public void connectionLost(Throwable t) {
t.printStackTrace();
}
public void deliveryComplete(IMqttDeliveryToken arg0) {
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Message received from broker...");
System.out.println("Received Message: -- ");
System.out.println(message.getPayload().toString());
}
}
MqttClientGenerator :
public class MqttClientGenerator {
private static final String BROKER_URI = "tcp://localhost:1883";
private static final String CLIENT_ID = "pub";
private static final String SUBSCRIBER_ID = "sub";
private MqttClientGenerator () {}
public static MqttClient generatePublisherClient() throws MqttException{
//adding timestamp to make client name unique every time
return new MqttClient(BROKER_URI, CLIENT_ID+new Date().getTime());
}
public static MqttClient generateSubscriberClient() throws MqttException{
//adding timestamp to make client name unique every time
return new MqttClient(BROKER_URI, SUBSCRIBER_ID+new Date().getTime());
}
}
what am i missing here?
Try deleting the line where you disconnect the client.
What I want?
I am trying to write an application where client sends a query and based on the query server gets twitter-stream and pushes to client.
What I have?
I have a simple structure in place where client can connect to server and server responds back
TweetStreamServer
package com.self.tweetstream;
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/tweets")
public class TweetStreamServer {
#OnMessage
public String tweets(final String message) {
return message;
}
}
TweetStreamClient
#ClientEndpoint
public class TweetStreamClient {
public static CountDownLatch latch;
public static String response;
#OnOpen
public void onOpen(Session session) {
try{
session.getBasicRemote().sendText("Hello World!");
} catch (IOException e) {
e.printStackTrace();
}
}
#OnMessage
public void printTweets(final String tweet) {
System.out.println("Tweet:" + tweet);
response = tweet;
latch.countDown();
}
}
TweetStreamTest
#Test
public void test() throws URISyntaxException, IOException, DeploymentException, InterruptedException {
System.out.println("URI: " + getEndpointUrl());
TweetStreamClient.latch = new CountDownLatch(1);
Session session = connectToServer(TweetStreamClient.class, "tweets");
assertNotNull(session);
assertTrue(TweetStreamClient.latch.await(10, TimeUnit.SECONDS));
assertEquals("Hello World!", TweetStreamClient.response);
}
Question
I am confused how can I now send continuous tweets that I receive from Twitter because my server method as per API is
#OnMessage
public String tweets(final String message) {
return message;
}
This means it expects a message in order return anything.
How can I send on-coming data from Twitter send to client?
This worked for me
#OnMessage
public void tweets(final String message, Session client) throws IOException, InterruptedException {
int i = 0;
for (Session peer : client.getOpenSessions()) {
while (i < 10) {
System.out.println("sending ...");
peer.getBasicRemote().sendText("Hello");
Thread.sleep(2000);
i++;
}
}
}
Thanks to Arun Gupta for helping through his tweets :)
As part of my lab this week I am suppose to convert a socket based chat application to RMI. So far I managed to connect server and client together and transfer data between them but the transfer is not continuous. What I mean is that when the client first connects t the server it broadcasts a message "X has entered the conversation" but that is all. Anything I type after that wont get broadcasted. I am about to pull out my hair. Please help.
public class ChatServer extends UnicastRemoteObject implements ChatMessage {
private static final long serialVersionUID = 1L;
private String sender;
private String message;
private ChatMessageType t;
public ChatServer() throws RemoteException {
super();
}
#Override
public void Message(String sender, ChatMessageType t, String message)
throws RemoteException {
this.sender = sender;
this.message = message;
this.t = t;
}
#Override
public String getSender() throws RemoteException {
return sender;
}
#Override
public String getMessage() throws RemoteException {
return message;
}
#Override
public ChatMessageType getType() throws RemoteException {
return t;
}
public String ToString() throws RemoteException{
String strMessage;
switch (t) {
case SETUP:
strMessage = sender + " has entered the conversation.";
break;
case TEARDOWN:
strMessage = sender + " has left the conversation.";
break;
case MESSAGE:
strMessage = sender + ": " + message;
break;
default:
strMessage = "";
}
return strMessage;
}
// driver.
public static void main(String arg[]) {
try {
ChatServer c = new ChatServer();
Registry registry = LocateRegistry.createRegistry(1099);
registry.rebind("Server", c);
System.out.println("Server bound in registry");
} catch (Exception e) {
System.out.println("Server error: " + e.getMessage());
e.printStackTrace();
}
}
}
public class ChatClient implements ActionListener {
// static private Socket c;
static ChatMessage obj = null;
// static private ObjectInputStream in;
// static private ObjectOutputStream out;
static private String name;
static private String host;
static private Integer port;
/**
* Launches this application
*/
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (args.length != 3) {
System.out
.println("Client requires exactly three args to run.");
System.exit(-1);
}
name = args[0];
host = args[1];
port = new Integer(args[2]);
final ChatClient application = new ChatClient();
application.getJFrame().setVisible(true);
try {
System.out.println("client: connecting to server...");
// c = new Socket(host, port);
obj = (ChatMessage) Naming.lookup("//" + host + ":" + port
+ "/Server");
System.out.println("client: connected!");
} catch (Exception e) {
System.out.println("client: " + e.getMessage());
System.exit(-1);
}
try {
// out = new ObjectOutputStream(c.getOutputStream());
// in = new ObjectInputStream(c.getInputStream());
// announce to other clients that you're here
// out.writeObject(new ChatMessage(name,
// ChatMessageType.SETUP, ""));
obj.Message(name, ChatMessageType.SETUP, "");
} catch (Exception e) {
}
// set up the client's listener as an anonymous thread that's
// always running
// new Thread(new Runnable(){
// public void run()
// {
// while(true)
// {
try {
System.out.println(name + ": waiting for data");
ChatMessage m = (ChatMessage) Naming.lookup("//" + host
+ ":" + port + "/Server");
System.out.println(name + ": data received");
application.updateTextArea(m.ToString());
} catch (Exception e) {
}
// }
// }
// }).start();
}
});
}
public void updateTextArea(final String message) {
conversation.setText(conversation.getText() + message + "\n");
// this will guarantee that the bottom of the conversation is visible.
conversation.setCaretPosition(conversation.getText().length());
}
// send button has been pressed, send the message to the server.
public void actionPerformed(ActionEvent e) {
if (send.getText().equals("Send")) {
try {
System.out.println(name + ": sending data");
// ChatMessage m = new ChatMessage(name,
// ChatMessageType.MESSAGE, message.getText());
// out.writeObject(m);
obj.Message(name, ChatMessageType.MESSAGE, message.getText());
message.setText(""); // clear the text box.
System.out.println(name + ": data sent");
} catch (Exception ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}
}
}
enum ChatMessageType{
SETUP,
MESSAGE,
TEARDOWN
}public interface ChatMessage extends Remote{
public String getSender() throws RemoteException;
public String getMessage() throws RemoteException;
public ChatMessageType getType() throws RemoteException;
public void Message(String sender, ChatMessageType t, String message) throws RemoteException;
public String ToString() throws RemoteException;
I realize this question is pretty old and you probably figured out an answer for this, but, I thought I'd share an approach I took for going from Java sockets to RMI. Maybe it is useful for others looking to do the same thing.
I basically abstracted out the socket portion into a "Tunnel" object that represents a communication path between hosts. And the tunnel consists of several "channels", that represent a one-way communication between the source and destination.
You can check out more details at my blog here: http://www.thecodespot.com/?p=1