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.
Related
I'm trying to set up a simple application using Spring and websockets and have problem setting up the connection.
I have looked around for examples but almost all searches lead to the chat sample which is not what I am trying to do.
My app is a task/monitoring scenario. I want to be able to send a request to the server and then monitor the progress
of the task on the same connection.
I have seen the chat sample, portfolio sample and various other comments on SO but I'm not sure what I'm missing.
I'm new to Spring and websockets.
The logging appears to show a successful connection for the /info path but then the call to stompClient.connect() fails to return.
14:02:26.330 [main] DEBUG org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport - Executing SockJS Info request, url=http://localhost:9080/Vault713MQServer/websocket/info
14:02:26.480 [main] DEBUG org.springframework.web.client.RestTemplate - Created GET request for "http://localhost:9080/Vault713MQServer/websocket/info"
14:02:26.559 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:9080/Vault713MQServer/websocket/info" resulted in 200 (OK)
14:02:26.578 [main] DEBUG org.springframework.web.socket.sockjs.client.WebSocketTransport - Starting WebSocket session url=ws://localhost:9080/Vault713MQServer/websocket/369/ee89fc87489842af868c0f0452aacf13/websocket
14:02:26.578 [main] DEBUG org.springframework.web.socket.client.standard.StandardWebSocketClient - Connecting to ws://localhost:9080/Vault713MQServer/websocket/369/ee89fc87489842af868c0f0452aacf13/websocket
14:02:26.693 [WebSocketClient-AsyncIO-1] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Processing SockJS open frame in WebSocketClientSockJsSession[id='ee89fc87489842af868c0f0452aacf13, url=ws://localhost:9080/Vault713MQServer/websocket]
14:02:26.693 [WebSocketClient-AsyncIO-1] DEBUG org.springframework.messaging.simp.stomp.DefaultStompSession - Connection established in session id=07e2d0cc-6f99-95d5-7014-614aad3e0f13
If I connect to 'http://localhost:9080/Vault713MQServer/websocket/info' in a browser it returns:
{"entropy":1894449220,"origins":["*:*"],"cookie_needed":true,"websocket":true}
On the server side I have:
/* WebSocketConfig.java */
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer
{
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
{
registry.addHandler(myHandler(), "/websocket").withSockJS();
}
#Bean
public ServerHandler myHandler()
{
return new ServerHandler();
}
}
/* ServerHandler.java */
public class ServerHandler extends TextWebSocketHandler
{
private final Logger logger = Logger.getLogger(this.getClass().getName());
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// TODO Auto-generated method stub
logger.log(Level.INFO, "Connection clodes with websocket server: session id {0}", session.getId());
super.afterConnectionClosed(session, status);
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// TODO Auto-generated method stub
logger.log(Level.INFO, "Connected user with websocket server: session id {0}", session.getId());
super.afterConnectionEstablished(session);
}
#Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
// TODO Auto-generated method stub
super.handleMessage(session, message);
}
#Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
// TODO Auto-generated method stub
super.handleTransportError(session, exception);
}
}
On the client side I have:
/* Clientside - Vault713MQClient.java */
public class Vault713MQClient
{
static public class MyStompSessionHandler
extends StompSessionHandlerAdapter
{
private String userId;
public MyStompSessionHandler(String userId)
{
this.userId = userId;
}
private void showHeaders(StompHeaders headers)
{
for (Map.Entry<String, List<String>> e : headers.entrySet())
{
System.err.print(" " + e.getKey() + ": ");
boolean first = true;
for (String v : e.getValue())
{
if (!first)
{
System.err.print(", ");
}
System.err.print(v);
first = false;
}
System.err.println();
}
}
private void sendJsonMessage(StompSession session)
{
session.send("/websocket", "hello from spring");
}
private void subscribeTopic(String topic, StompSession session)
{
session.subscribe(topic, new StompFrameHandler()
{
#Override
public Type getPayloadType(StompHeaders headers)
{
return String.class;
}
#Override
public void handleFrame(StompHeaders headers,
Object payload)
{
System.err.println(payload.toString());
}
});
}
#Override
public void afterConnected(StompSession session,
StompHeaders connectedHeaders)
{
System.err.println("Connected! Headers:");
showHeaders(connectedHeaders);
// subscribeTopic("/topic/messages", session);
// sendJsonMessage(session);
}
}
public static void main(String args[]) throws Exception
{
WebSocketClient simpleWebSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>(1);
transports.add(new WebSocketTransport(simpleWebSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
String url = "ws://localhost:9080/Vault713MQServer/websocket";
String userId = "spring-" + ThreadLocalRandom.current().nextInt(1, 99);
StompSessionHandler sessionHandler = new MyStompSessionHandler(userId);
StompSession session = stompClient.connect(url, sessionHandler).get();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;)
{
System.out.print(userId + " >> ");
System.out.flush();
String line = in.readLine();
if (line == null)
{
break;
}
if (line.length() == 0)
{
continue;
}
session.send("/websocket", line);
// ClientMessage msg = new ClientMessage(userId, line);
// session.send("/app/chat/java", msg);
}
}
}
Can anyone see what I have done wrong or does anyone have a simple complete example of what I am trying to do?
Many thanks.
KCM
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 :)
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.