Howto add request header to Tyrus annotation based client - java

I'm trying to access a websocket server endpoint using a tyrus standalone client (tyrus-standalone-client-1.9) with an annotation based client endpoint. I was mainly following this example.
That is, my client endpoint currently looks like
#ClientEndpoint
public class MyClientEndpoint {
private static CountDownLatch latch;
private Logger logger = Logger.getLogger(this.getClass().getName());
#OnOpen
public void onOpen(Session session) throws Exception {
session.getBasicRemote().sendText("initialRequest")
}
#OnMessage
public void onMessage(String message, Session session) throws Exception {
// do something
}
#OnClose
public void onClose(Session session, CloseReason closeReason) {
logger.info(String.format("Session %s close because of %s", session.getId(), closeReason));
latch.countDown();
}
public static void main(String[] args) {
latch = new CountDownLatch(1);
ClientManager client = ClientManager.createClient();
try {
URI serverEndpointUri = new URI("ws://localhost/websockets/server/endpoint");
client.connectToServer(MyClientEndpoint.class, serverEndpointUri);
latch.await();
} catch (DeploymentException | URISyntaxException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
However I need to pass some session ID along with the request and I need to modify the origin header of the request to get my connection accepted by the server endpoint.
In a programmatic client endpoint I could do something like
final Builder configBuilder = ClientEndpointConfig.Builder.create();
configBuilder.configurator(new Configurator() {
#Override
public void beforeRequest(final Map<String, List<String>> headers) {
headers.put("Cookie", Arrays.asList("X-Session=0f822c8c-bf63-4ae7-9d2f-af263f86baad"));
headers.put("Origin", Arrays.asList("http://localhost"));
}
});
ClientEndpointConfig clientConfig = configBuilder.build();
ClientManager client = ClientManager.createClient();
URI serverEndpointUri = new URI("ws://localhost/websockets/server/endpoint");
client.connectToServer(new MyClientEndpoint(), clientConfig, serverEndpointUri);
But there doesn't seem to be any option to pass the configuration to an annotation based client.
Is there some other way to add/modify the request headers that I'm currently missing? I'd really like to stay with the annotation based approach as it seems to be much cleaner to me...

See ModifyRequestResponseHeadersTest.java:183
#ClientEndpoint(configurator = MyClientConfigurator.class)
public static class MyClientEndpoint {
public static final CountDownLatch messageLatch = new CountDownLatch(1);
public static volatile String receivedMessage;
#OnOpen
public void onOpen(Session session) throws IOException {
session.getBasicRemote().sendText(SENT_MESSAGE);
}
#OnMessage
public void onMessage(String message) {
receivedMessage = message;
messageLatch.countDown();
}
}
And MyClientConfigurator:
public static class MyClientConfigurator extends ClientEndpointConfig.Configurator {
static volatile boolean called = false;
#Override
public void beforeRequest(Map<String, List<String>> headers) {
called = true;
headers.put(HEADER_NAME, Arrays.asList(HEADER_VALUE));
headers.put("Origin", Arrays.asList("myOrigin"));
}
#Override
public void afterResponse(HandshakeResponse handshakeResponse) {
final Map<String, List<String>> headers = handshakeResponse.getHeaders();
assertEquals(HEADER_VALUE[0], headers.get(HEADER_NAME).get(0));
assertEquals(HEADER_VALUE[1], headers.get(HEADER_NAME).get(1));
assertEquals(HEADER_VALUE[2], headers.get(HEADER_NAME).get(2));
assertEquals("myOrigin", headers.get("origin").get(0));
}
}

Related

Java WebSocketStompClient connect not returning

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

SparkJava websocket not working

SparkJava web sockets won't work. Whenever I attempt to connect to it with a websocket tester, at 'ws://localhost:4567/echo' it gets an error 'undefined' and never connects, nor do any of the sout's or printStackTrace get called.
#WebSocket
public class EchoWebSocket {
private static final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
#OnWebSocketConnect
public void connected(Session session) {
System.out.println("Client connected");
//sessions.add(session);
}
#OnWebSocketClose
public void closed(Session session, int statusCode, String reason) {
System.out.println("Client disconnected");
//sessions.remove(session);
}
#OnWebSocketMessage
public void message(Session session, String message) throws IOException {
System.out.println("Got: ");// + message); // Print message
//session.getRemote().sendString(message); // and send it back
}
#OnWebSocketError
public void throwError(Throwable error) {
error.printStackTrace();
}
}
how I call it
Spark.webSocket("/echo", new EchoWebSocket());
Spark.init();
You need to define the class, not create an object.
Spark.webSocket("/echo", EchoWebSocket.class);

unable to subscribe paho mqtt java

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.

Java Eclipse Paho Implementation - Auto reconnect

I'm trying to implement eclipse.paho in my project to connect Mqtt Broker (Both subscribing and publishing purpose). The problem is, when I using the subscribing feature (Implementing MqttCallback interface), I couldn't figure our how can I reconnect if the connection lost. MqttCallback interface has a connectionLost method, but it is useful for the debug what causes the connection lost. I searched but couldn't find a way to establish auto reconnect. Can you suggest a way or document about this problem?
I'm using the paho client 1.2.0.
With the MqttClient.setAutomaticReconnect(true) and interface MqttCallbackExtended API, and thanks to https://github.com/eclipse/paho.mqtt.java/issues/493, I could manage to reconnect automatically when the connection to broker is down.
See below the code.
//Use the MqttCallbackExtended to (re-)subscribe when method connectComplete is invoked
public class MyMqttClient implements MqttCallbackExtended {
private static final Logger logger = LoggerFactory.getLogger(MqttClientTerni.class);
private final int qos = 0;
private String topic = "mytopic";
private MqttClient client;
public MyMqttClient() throws MqttException {
String host = "tcp://localhost:1883";
String clientId = "MQTT-Client";
MqttConnectOptions conOpt = new MqttConnectOptions();
conOpt.setCleanSession(true);
//Pay attention here to automatic reconnect
conOpt.setAutomaticReconnect(true);
this.client = new org.eclipse.paho.client.mqttv3.MqttClient(host, clientId);
this.client.setCallback(this);
this.client.connect(conOpt);
}
/**
* #see MqttCallback#connectionLost(Throwable)
*/
public void connectionLost(Throwable cause) {
logger.error("Connection lost because: " + cause);
/**
* #see MqttCallback#deliveryComplete(IMqttDeliveryToken)
*/
public void deliveryComplete(IMqttDeliveryToken token) {
}
/**
* #see MqttCallback#messageArrived(String, MqttMessage)
*/
public void messageArrived(String topic, MqttMessage message) throws MqttException {
logger.info(String.format("[%s] %s", topic, new String(message.getPayload())));
}
public static void main(String[] args) throws MqttException, URISyntaxException {
MyMqttClient s = new MyMqttClient();
}
#Override
public void connectComplete(boolean arg0, String arg1) {
try {
//Very important to resubcribe to the topic after the connection was (re-)estabslished.
//Otherwise you are reconnected but you don't get any message
this.client.subscribe(this.topic, qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
The best way to do this is to structure your connection logic so it lives in a method on it's own so it can be called again from the connectionLost callback in the MqttCallback instance.
The connectionLost method is passed a Throwable that will be the exception that triggered the disconnect so you can make decisions about the root cause and how this may effect when/how you reconnect.
The connection method should connect and subscribe to the topics you require.
Something like this:
public class PubSub {
MqttClient client;
String topics[] = ["foo/#", "bar"];
MqttCallback callback = new MqttCallback() {
public void connectionLost(Throwable t) {
this.connect();
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("topic - " + topic + ": " + new String(message.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
};
public static void main(String args[]) {
PubSub foo = new PubSub();
}
public PubSub(){
this.connect();
}
public void connect(){
client = new MqttClient("mqtt://localhost", "pubsub-1");
client.setCallback(callback);
client.connect();
client.subscribe(topics);
}
}
To use auto reconnect, just set setAutomaticReconnect(true) on the MqttConnectOptions object.
MqttAndroidClient mqttClient = new MqttAndroidClient(context, mqttUrl, clientId);
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttClient.connect(mqttConnectOptions, null, mqttActionListener());

Java EE Websocket: How to send keep sending data without receiving message?

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 :)

Categories