how to implement WebSocket client reconnect after some time in Vertx? - java

I'm connecting websocket server using vertx, but when connection closed/Server went down(temporarily) try to reconnect after some interval.
WebSocket code is :
public void start() throws Exception {
HttpClient client = vertx.createHttpClient();
MultiMap form = MultiMap.caseInsensitiveMultiMap();
if(MapUtils.isNotEmpty(header)) {
header.forEach((k,v)-> {
form.set(k, v);
});
}
RequestOptions options = new RequestOptions()
.setHost(host)
.setPort(port)
.setSsl(sslFlag)
.setURI(uri);
Handler<WebSocket> socket = new Handler<WebSocket>() {
#Override
public void handle(WebSocket event) {
event.handler(data -> {
currentCount = 0;
LOGGER.info("Received data " + data.toString("ISO-8859-1"));
});
}
};
Handler<Throwable> exception = new Handler<Throwable>() {
#Override
public void handle(Throwable event) {
LOGGER.error("Exception occured in Websocket connection, retrying count is {}", currentCount, event);
}
};
client.websocket(options, form, socket , exception);
}
Above code Port, Host, etc are configured. Please any one suggest how to reconnect?

You can close the client and restart it:
public void start() {
startClient(this.vertx);
}
private void startClient(Vertx vertx) {
HttpClient client = vertx.createHttpClient();
client.webSocket(8080, "localhost", "/", (ar) -> {
if (ar.succeeded()) {
WebSocket ctx = ar.result();
ctx.textMessageHandler((msg) -> {
System.out.println("Client " + msg);
ctx.writeTextMessage("pong");
}).exceptionHandler((e) -> {
System.out.println("Restarting in 10 seconds");
client.close();
vertx.setTimer(10 * 1000, (__) -> {
startClient(vertx);
});
});
} else {
System.out.println(ar.cause());
}
});
}
Edited: Any exception when connecting time also retry will happen in below code.
private void startClient(Vertx vertx) {
HttpClient client = vertx.createHttpClient();
MultiMap form = MultiMap.caseInsensitiveMultiMap();
if(MapUtils.isNotEmpty(header)) {
header.forEach((k,v)-> {
form.set(k, v);
});
}
RequestOptions options = new RequestOptions()
.setHost(host)
.setPort(port)
.setSsl(sslFlag)
.setURI(uri);
client.websocket(options, form, (ctx) ->{
ctx.textMessageHandler( msg -> {
System.out.println("Client " + msg);
}).exceptionHandler((e) -> {
client.close();
vertx.setTimer(waitTime * 10 * 1000, (__) -> {
startClient(vertx);
});
});
}, (exec) -> {
vertx.setTimer(waitTime * 10 * 1000, (__) -> {
startClient(vertx);
});
});
}

Related

How to stop and start a PubSub programatically through the MessageReceiver in Java

Is it possible to pause and start a GCP PubSub Subscriber(pull) programatically using Java?
I have the following code for the MessageReceiver:
MessageReceiver receiver = (message, consumer) -> {
System.out.println("Received message: " + message.getData().toStringUtf8() + " at " + LocalDateTime.now());
Mono<Void> mono = Mono.just(message.getData().toStringUtf8())
.flatMap(deserializePayload())
.doOnSuccess(creditNoteRequestDTO -> configureMDC(message.getAttributesMap(), creditNoteRequestDTO.getOrderNumber()))
.doOnError(error -> log.error("Problem while parsing CreditNoteRequest DTO {}", message.getData().toStringUtf8(), error))
.map(creditNoteRequestMapper::mapToCreditNoteRequest)
.flatMap(creditNoteRequestProcess::process)
.doOnError(throwable -> {
if (throwable instanceof CallNotPermittedException) ConstantUtils.ENABLED_SUBSCRIBER = false;
consumer.nack();
})
.doOnSuccess(unused -> {
log.info("Doing ACK on payload {}", message.getData().toStringUtf8());
consumer.ack();
});
if (Boolean.TRUE.equals(ConstantUtils.ENABLED_SUBSCRIBER)) {
mono.subscribe();
} else {
System.err.println("Subscription has been disabled.");
//mono.subscribe().dispose();
try {
System.err.println("PRE: " + LocalDateTime.now() + " " + ConstantUtils.ENABLED_SUBSCRIBER);
TimeUnit.SECONDS.sleep(15);
ConstantUtils.ENABLED_SUBSCRIBER = true;
System.err.println("POST: " + LocalDateTime.now() + " " + ConstantUtils.ENABLED_SUBSCRIBER);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
And the following for the subscriber:
Subscriber subscriber = null;
FlowControlSettings controlSettings = FlowControlSettings
.newBuilder()
.setMaxOutstandingElementCount(2000L)
.build();
subscriber = Subscriber
.newBuilder(subscription, receiver)
.setFlowControlSettings(controlSettings)
.build();
// Start the subscriber.
subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", subscription);
System.out.printf("Max elements count: %s\n", subscriber.getFlowControlSettings().getMaxOutstandingElementCount());
// Allow the subscriber to run for 45s unless an unrecoverable error occurs.
Subscriber finalSubscriber = subscriber;
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
System.err.println("Subscriber has been shut down.");
finalSubscriber.stopAsync();
}
},
120000
);
It is somehow possible to use finalSubscriber.stopAsync(); on the MessageReceiver or pause the messageReceiver?
UPDATE: Thanks, I managed to stop it, but how do I restart it? Now, when I try to restart, nothing happens.
private void createSubscriber() {
ProjectSubscriptionName subscription = ProjectSubscriptionName.of("txd-boss-dev", "circuit-breaker-test-sub");
this.subscriber = Subscriber.newBuilder(subscription, getMessageReceiver()).build();
}
private void runSubscriber(boolean start) {
if(start) {
try {
this.subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", this.subscriber.getSubscriptionNameString());
subscriber.awaitTerminated(240, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.err.println("Shutting down subscription.");
runSubscriber(false);
}
} else {
subscriber.stopAsync();
}
}
#EventListener(ContextRefreshedEvent.class)
public void test() {
createSubscriber();
runSubscriber(true);
}
You need to return the same subscriber object to start and stop it:
check some google examples here.
here is a sketch (adapt for your class):
private Subscriber StartSubscriber() {
Subscriber subscriber = null;
try {
subscriber = Subscriber.newBuilder(subscriptionName, receiver).build();
subscriber.startAsync().awaitRunning();
System.out.printf("Listening for messages on %s:\n", subscriptionName.toString());
subscriber.awaitTerminated(30, TimeUnit.SECONDS);
return subscriber;
} catch (TimeoutException timeoutException) {
// Shut down the subscriber after 30s. Stop receiving messages.
System.out.printf("Error \n");
subscriber.stopAsync();
}
}
private void StopSubscriber(Subscriber subscriber) {
System.out.printf("Stoping subscriber\n");
subscriber.stopAsync();
}
public void test() {
Subscriber subscriber = StartSubscriber();
StopSubscriber(subscriber);
}

Vertx event bus interceptor blocking the request call

I am trying to send data over event bus when got the call from client and response back to client.
Everything is working fine until i add the interceptor on event bus..
Here is the Code:-
public class TestVerticle extends AbstractVerticle {
#Override
public void start(Promise<Void> promise) {
Router router = Router.router(vertx);
vertx.exceptionHandler(globalExceptionHandler -> {
System.out.println("Exception not handled in application : " + globalExceptionHandler.getCause());
});
vertx.eventBus().consumer("test", handler -> {
System.out.println("Message receive form event bus : " + handler.body().toString());
handler.reply(handler.body().toString());
});
router.get().handler(this::rootHandler);
vertx.createHttpServer().requestHandler(router).listen(8080, resultHandler -> {
if (resultHandler.succeeded()) {
promise.complete();
} else {
promise.fail(resultHandler.cause());
}
});
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
});
// vertx.eventBus().addInboundInterceptor(handler -> {
// System.out.println("Inbound data : " + handler.body().toString());
// });
}
private void rootHandler(RoutingContext routingContext) {
JsonObject msg = new JsonObject().put("path", routingContext.request().path());
vertx.eventBus().request("test","Hello from Application",reply -> this.replyHandler(routingContext,reply));
}
private void replyHandler(RoutingContext ctx, AsyncResult<Message<Object>> reply) {
HttpServerResponse response = ctx.response()
.putHeader("Content-Type", "application/json");
System.out.println("Reply from event bus : " +reply.result().body().toString());
if (reply.succeeded()) {
response.setStatusCode(200)
.setStatusMessage("OK")
.end(reply.result().body().toString());
} else {
response.setStatusCode(500)
.setStatusMessage("Server Error")
.end(new JsonObject().put("error", reply.cause().getLocalizedMessage()).encodePrettily());
}
}
public static void main(String[] args) {
Vertx.vertx().deployVerticle(new TestVerticle(), deploymenHandler -> {
if (deploymenHandler.succeeded()) {
System.out.println("verticle deplyed");
} else {
System.out.println("failed");
}
});
}
#Override
public void stop(Promise<Void> promise) {
System.out.println("Exiting verticle");
promise.complete()
}
}
One more doubt is when I stop the application from IDE the Stop method is not called, but If I undeploy this verticle from another verticle that is working fine.
When you are using eventbus interceptors you will have to call the next() method in the handler otherwise you will get stuck there.
vertx.eventBus().addOutboundInterceptor(handler -> {
System.out.println("Outbound data : "+handler.body().toString());
handler.next();
});
vertx.eventBus().addInboundInterceptor(handler -> {
System.out.println("Inbound data : "+handler.body().toString());
handler.next();
});

Where I may wrong while connected to StompClient?

I need help with connect on android to my WebSocket server based on Spring boot. Source code of this server I have taken https://spring.io/guides/gs/messaging-stomp-websocket/
Everything works fine on server and browser client on this sample,
but if I use StompClient (https://github.com/NaikSoftware/StompProtocolAndroid) to connect on my socket I am getting mStompClient.isConnected() == false and conand mStompClient.send(...) doesn't send anything (?).
After few minutes socketweb server closes the connection and I get in my log: '~~ Stomp connection closed'.
Web server locates on Heroku cloud system.
There is my connecting code from android activity:
private StompClient mStompClient;
private void connectStomp(){
mStompClient = Stomp.over(WebSocket.class, "wss://myserver/gs-guide-websocket");
mStompClient.topic("/topic/greetings").subscribe(new Action1<StompMessage>() {
#Override
public void call(StompMessage stompMessage) {
Log.w(TAG, "== "+stompMessage.getPayload());
}
});
mStompClient.connect();
mStompClient.lifecycle().subscribe(new Action1<LifecycleEvent>() {
#Override
public void call(LifecycleEvent lifecycleEvent) {
switch (lifecycleEvent.getType()) {
case OPENED:
Log.w(TAG, "~~ Stomp connection opened");
break;
case ERROR:
Log.e(TAG, "~~ Error", lifecycleEvent.getException());
break;
case CLOSED:
Log.w(TAG, "~~ Stomp connection closed "+lifecycleEvent.getMessage());
break;
}
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
connectStomp();
}
// Send test request to server
public void onSend(View view){
Log.w(TAG,"onSend: click");
mStompClient.send("/app/hello","Test").subscribe(new Observer<Void>() {
#Override
public void onCompleted() {
Log.w(TAG, "~~~~ onCompleted");
}
#Override
public void onError(Throwable e) {
Log.w(TAG, "~~~~ onCompleted "+e.getMessage());
}
#Override
public void onNext(Void aVoid) {
Log.w(TAG, "~~~~ onNext ");
}
});
if (mStompClient.isConnected()){
mStompClient.send("/app/hello","test msg").subscribe();
Log.w("aaaa : ","onCreate: connected");
}
}
It would be my mistake but if I connect to my server socket with spring boot WebSocketStompClient everithing works fine:
private SockJsClient sockJsClient;
private WebSocketStompClient stompClient;
private final WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
#Before
public void setup() {
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
this.sockJsClient = new SockJsClient(transports);
this.stompClient = new WebSocketStompClient(sockJsClient);
this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());
}
#Test
public void getGreeting() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> failure = new AtomicReference<>();
StompSessionHandler handler = new TestSessionHandler(failure) {
#Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/greetings", new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers) {
return Greeting.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
Greeting greeting = (Greeting) payload;
try {
System.out.println(greeting.getContent());
assertEquals("Hello, Spring!", greeting.getContent());
} catch (Throwable t) {
System.out.println(t.getMessage());
failure.set(t);
} finally {
session.disconnect();
latch.countDown();
}
}
});
try {
session.send("/app/hello", "Test");
} catch (Throwable t) {
failure.set(t);
latch.countDown();
}
}
};
this.stompClient.connect("wss://myserver/gs-guide-websocket", this.headers, handler, 443);
if (latch.await(10, TimeUnit.SECONDS)) {
if (failure.get() != null) {
throw new AssertionError("", failure.get());
}
}
else {
fail("Greeting not received");
}
}
private class TestSessionHandler extends StompSessionHandlerAdapter {
private final AtomicReference<Throwable> failure;
public TestSessionHandler(AtomicReference<Throwable> failure) {
this.failure = failure;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
this.failure.set(new Exception(headers.toString()));
}
#Override
public void handleException(StompSession s, StompCommand c, StompHeaders h, byte[] p, Throwable ex) {
this.failure.set(ex);
}
Any ideas? Thanks a lot!
I used the same Library in order to connect with Stomp based web socket. There were some configurations on the server side. On the android side, I was using URL as starting with "ws://" and ending with "websocket" like "ws://" + SERVER_URL + "/websocket".
See this answer for server side https://stackoverflow.com/a/41751897/5392825

"Connection refused" Autobahn Android to Java EE Endpoint

I have a Java EE Endpoint set up on a Payara server to which I attempt to connect an Android client using Autobahn WebSockets. I have the following setup:
My WebSocket Endpoint on the server:
public class CommunicationSocket extends Endpoint {
#Override
public void onOpen(Session aSession, EndpointConfig aConfig) {
aSession.addMessageHandler(new MessageHandler.Whole<byte[]>() {
#Override
public void onMessage(byte[] aMessage) {
// Do something fun
}
});
}
}
I register the WebSocket as such:
public class RegisterSocket implements ServerApplicationConfig {
#Override
public Set<ServerEndpointConfig> getEndpointConfigs(
Set<Class<? extends Endpoint>> aEndpointClasses) {
Set<ServerEndpointConfig> result = new HashSet<>();
for (Class endpointClass : aEndpointClasses) {
if (endpointClass.equals(CommunicationSocket.class)) {
ServerEndpointConfig sec
= ServerEndpointConfig.Builder.create(endpointClass,
"/WebSocket").build();
result.add(sec);
}
}
return result;
}
#Override
public Set<Class<?>> getAnnotatedEndpointClasses(
Set<Class<?>> aScanned) {
return Collections.emptySet();
}
}
The WebSocket can now be reached att ws://localhost:46588/Server/WebSocket. I've confirmed that it works with the following javascript in chrome:
function WebSocketTest() {
if ("WebSocket" in window) {
ws = new WebSocket("ws://localhost:46588/Server/WebSocket");
ws.onopen = function() {
ws.send("Message to send");
alert("Message is sent...");
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
alert("Message is received... \n" + received_msg);
};
} else {
// The browser doesn't support WebSocket
alert("WebSocket NOT supported by your Browser!");
}
}
However when I try to connect with my android client using autobahn, the socket closes without being able to establish a connection with the message "Connection refused". I use the following code in onCreate to connect (mSocket is a field of the activity class):
WebSocketHandler messageHandler = new WebSocketHandler() {
#Override
public void onOpen() {
System.out.println("Connected...");
}
#Override
public void onClose(int code, String reason) {
System.out.println("Socket closing: " + reason);
mSocket = null;
}
#Override
public void onTextMessage(String payload) {
System.out.println("Received: " + payload);
}
#Override
public void onBinaryMessage(byte[] payload) {
System.out.println("Message received: " + payload);
}
};
mSocket = new WebSocketConnection();
try {
String address = "ws://localhost:46588/Server/WebSocket";
mSocket.connect(address, messageHandler);
} catch (WebSocketException e) {
System.out.println("Could not connect to WebSocket: "
+ e.getMessage());
mSocket = null;
}
I have internet permissions i my manifest as so:
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
Is there anything in my code or approach that is wrong? Is it possible to log/catch the event of connecting (handshake or what not) in order to shed light onto why the connection is refused?
And as I got ready to post the question I googled one last time: lo and behold, localhost is not the localhost of the machine running the emulator (I guess it's the localhost of the emulated device). So use:
10.0.2.2:[port]
instead of
localhost:[port]
and it will be fine on the emulator (don't forget to change if running on an actual device)

Problems with Jetty websocket and HTML5

I created a simple chat server in Java with a Javascript client , but I'm having a problem receiving messages from JavaScript to Java.
When i use the Java client it is work fine, but when i use Javascript i have the problem.
My code:
Java
public class ChatRunner {
public static void main(String[] args) throws Exception {
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler(
ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
try {
ServerContainer wscontainer = WebSocketServerContainerInitializer
.configureContext(context);
wscontainer.addEndpoint(ChatEndpoint.class);
server.start();
server.dump(System.err);
server.join();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
#ServerEndpoint(value="/events/")
public class ChatEndpoint {
#OnOpen
public void onWebSocketConnect(Session sess) {
System.out.println("Socket Connected: " + sess);
}
#OnMessage
public void onWebSocketText(String message) {
System.out.println("Received TEXT message: " + message);
}
#OnClose
public void onWebSocketClose(CloseReason reason) {
System.out.println("Socket Closed: " + reason);
}
#OnError
public void onWebSocketError(Throwable cause) {
cause.printStackTrace(System.err);
}
}
JavaScript:
var ws = new WebSocket("ws://localhost:8080/events/");
ws.onopen = function() { ws.send("Hello"); };
Well you can test your server using https://www.websocket.org/echo.html. Simply put your global url and test.

Categories

Resources