I want to read the messages received by a websocket in onmessage event. I follow the explanation made in this thread
I only achieved to add a Listener and be informed when the websocket is created but I donĀ“t know how to read the messages received by the websocket in onmessage event.
Here is my code:
public class TestBet365Socket {
public static void main(String[] args) throws Exception {
/* turn off annoying htmlunit warnings */
java.util.logging.Logger.getLogger("com.gargoylesoftware").setLevel(java.util.logging.Level.OFF);
WebClient client = new WebClient(BrowserVersion.CHROME);
client.getOptions().setCssEnabled(false);
client.getOptions().setJavaScriptEnabled(true);
client.getOptions().setThrowExceptionOnScriptError(false);
client.getInternals().addListener(new Listener() {
#Override
public void webSocketCreated(WebSocket arg0) {
System.out.println("Websocket Created " + arg0);
}
});
HtmlPage page = client.getPage("https://mobile.bet365.com/Default.aspx?lng=3");
client.waitForBackgroundJavaScript(10000);
List<NameValuePair> response =page.getWebResponse().getResponseHeaders();
for (NameValuePair header : response) {
System.out.println(header.getName() + " = " + header.getValue());
}
System.out.println(page.asText());
client.close();
}
}
You have to do two things:
implement you own listener
Create a class that implements org.eclipse.jetty.websocket.api.WebSocketListener. Inside this class you can implement you way of capturing the socket communication.
set the listener
The method webSocketCreated() gots a WebSocket instance as parameter. Call setWebSocketListener(yourListener) with the listener you have created in the first step.
Now you are done and your listener will be called every time the websocket gots data.
Related
I'm looking to trigger two messages sent back to a micro service from the one call.
I want the response to be sent firstly (its an acceptance message) and then the contents from the sendPort2Response method to be sent, the current functionality sends the response last - is this even possible to do?
public Port2RequestResponse processPort2Request(Port2Request request) {
FMNP getFMNPRequestObject = unmarshalInputRequest(request.getXmlPortMessage());
Port2RequestResponse response = new Port2RequestResponse();
response.setResult(getSuccessResponse(getFMNPRequestObject.getPort().getDonor()));
Thread t = new Thread(){
#SneakyThrows
#Async
public void run(){
sendPort2Response(request);
Thread.sleep(10000);
}
};
t.start();
return response;
}
I'm trying to create a websocket and dynamically recalculate its header in every message sent. Is it possible?
I was trying to use an interceptor but is only called once.
public void run() {
// only open a websocket if there aren't websockets already open
if (this.webSocket == null || !this.openingWS) {
this.openingWS = true;
wsBuilder = new OkHttpClient.Builder();
OkHttpClient client = wsBuilder.addInterceptor(this)
.readTimeout(0, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url("wss://...")
.build();
client.newWebSocket(request, this);
// Trigger shutdown of the dispatcher's executor so this process can exit cleanly.
client.dispatcher().executorService().shutdown();
}
}
#Override public void onOpen(WebSocket webSocket, Response response) {
this.openingWS = false; // already open
this.webSocket = webSocket; // storing websocket for future usages
if (listener != null) listener.onWSOpen();
}
public void sendCommand(String cmd) {
System.out.println("SEND " + cmd);
if (webSocket != null) webSocket.send(cmd);
}
This same class is implementing the interceptor
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
if (!isSpecial()) return chain.proceed(originalRequest);
okhttp3.Request.Builder builder = originalRequest.newBuilder()
.addHeader("text", "...")
.addHeader("dfds", "...");
Request compressedRequest = builder.build();
return chain.proceed(compressedRequest);
}
The authentication code sent in the header will change every X seconds/minutes.
If it's not possible to change dynamically the header, what is the best way to approach this kind of connection?
Thank you for your help.
I think the headers are send only first time when you request the connection, later is depends on frames between the client and the server.
So if you want to inform the server that you had changed the header then send message with your new header. Or you can close the connection and start a new one with the new header.
I'm facing the following problem and I found no working solution yet.
I have 3 different applications that should communicate with each other:
the UI part (1)
the backend application (2)
the microservice "in the cloud" (3)
The backend application provides a Webservice (REST) for the UI to get and put information from/to the microservice.
Everything I want to grab from the microservice works fine, but:
If I want to put data to the microservice, the specs require a websocket connection. This works fine too, but the microservice returns a message after the (un-)successful command, like
{"statusCode":200,"messageId":"1234567890"}
The problem now is: How can I grab this message in my application and send it back to the UI, so the user knows if the command was successful?
For the moment I tried this:
WebSocketClient.java
#OnMessage
public void onMessage(Session session, String msg) {
if (this.messageHandler != null) {
this.messageHandler.handleMessage(msg);
}
}
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
public static interface MessageHandler {
public String handleMessage(String message);
}
MyTotalAwesomeController.java
public class MyTotalAwesomeController {
WebSocketClient wsc = new WebSocketClient();
...
#RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
public ResponseEntity<Object> putDataToMicroservice(#RequestBody Map<String, Object> payload, #RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
...
wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
public String handleMessage(String message) {
System.out.println("RETURN MSG FROM WSS : " + message);
return message;
}
});
return ResponseEntity.ok("worked");
}
I can see the console output from the MessageHandler return, but I don't know how I can pass this to the parent method for return insted of just returning the ResponseEntity.ok().
I'm not very used to WebSocket connections in Java yet, so please don't judge me ;-)
Thank you for your help.
The code below will work under the assumption that the #OnMessage method is executed in a thread managed by the WebSocket client runtime. Please inspect the thread that runs the #OnMessage method.
If the above premise is true, the putDataToMicroservice() method, executed by a thread in the global scope, will wait until the WebSocket response arrives at the WS client thread, which will repass the message to the global scope thread. Then the execution in your controller class will continue.
public class MyTotalAwesomeController {
WebSocketClient wsc = new WebSocketClient();
// Queue for communication between threads.
private BlockingQueue<String> queue;
#PostConstruct
void init() {
queue = new SynchronousQueue<>(true);
// This callback will be invoked by the WebSocket thread.
wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
#Override
public String handleMessage(String message) {
System.out.println("RETURN MSG FROM WSS : " + message);
// Pass message to the controller thread.
queue.put(message);
// Note that the return value is not necessary.
// You can take it out of the interface as well.
return null;
}
});
}
#RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
public ResponseEntity<Object> putDataToMicroservice(#RequestBody Map<String, Object> payload, #RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
// At this point you make a WebSocket request, is that right?
doWebSocketRequest();
// This poll call will block the current thread
// until the WebSocket server responds,
// or gives up waiting after the specified timeout.
//
// When the WebSocket server delivers a response,
// the WS client implementation will execute the
// #OnMessage annotated method in a thread
// managed by the WS client itself.
//
// The #OnMessage method will pass the message
// to this thread in the queue below.
String message = queue.poll(30, TimeUnit.SECONDS);
if (message == null) {
// WebSocket timeout.
}
return ResponseEntity.ok("worked");
}
}
I want to pass messages to bus via REST, and get it back. But I cant correctly setup the message bus receiver, it throws java.lang.IllegalStateException: Response has already been written. In real life message bus should receive messages from different sources and pass a message to another target. Therefore we just need to publish the message to the bus. But how to correctly read messages and handle all of them? For example from a REST interface: read that messages!
My simple app start:
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new RESTVerticle());
vertx.deployVerticle(new Receiver());
EventBus eventBus = vertx.eventBus();
eventBus.registerDefaultCodec(MessageDTO.class, new CustomMessageCodec());
}
REST part
public class RESTVerticle extends AbstractVerticle {
private EventBus eventBus = null;
#Override
public void start() throws Exception {
Router router = Router.router(vertx);
eventBus = vertx.eventBus();
router.route().handler(BodyHandler.create());
router.route().handler(CorsHandler.create("*")
.allowedMethod(HttpMethod.GET)
.allowedHeader("Content-Type"));
router.post("/api/message").handler(this::publishToEventBus);
// router.get("/api/messagelist").handler(this::getMessagesFromBus);
router.route("/*").handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(9999);
System.out.println("Service running at 0.0.0.0:9999");
}
private void publishToEventBus(RoutingContext routingContext) {
System.out.println("routingContext.getBodyAsString() " + routingContext.getBodyAsString());
final MessageDTO message = Json.decodeValue(routingContext.getBodyAsString(),
MessageDTO.class);
HttpServerResponse response = routingContext.response();
response.setStatusCode(201)
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(message));
eventBus.publish("messagesBus", message);
}
And the Receiver: I move it to a different class, but it does not help
public class Receiver extends AbstractVerticle {
#Override
public void start() throws Exception {
EventBus eventBus = vertx.eventBus();
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(CorsHandler.create("*")
.allowedMethod(HttpMethod.GET)
.allowedHeader("Content-Type"));
router.get("/api/messagelist").handler(this::getMessagesFromBus);
router.route("/*").handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(9998);
System.out.println("Service Receiver running at 0.0.0.0:9998");
private void getMessagesFromBus(RoutingContext routingContext) {
EventBus eventBus = vertx.eventBus();
eventBus.consumer("messagesBus", message -> {
MessageDTO customMessage = (MessageDTO) message.body();
HttpServerResponse response = routingContext.response();
System.out.println("Receiver ->>>>>>>> " + customMessage);
if (customMessage != null) {
response.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(customMessage));
}
response.closed();
});
}
So if i post message to REST and handler publish it to the bus, when I am runtime get http://localhost:9998/api/messagelist it is return json, but second time it trow exception
java.lang.IllegalStateException: Response has already been written
at io.vertx.core.http.impl.HttpServerResponseImpl.checkWritten(HttpServerResponseImpl.java:561)
at io.vertx.core.http.impl.HttpServerResponseImpl.putHeader(HttpServerResponseImpl.java:154)
at io.vertx.core.http.impl.HttpServerResponseImpl.putHeader(HttpServerResponseImpl.java:52)
at com.project.backend.Receiver.lambda$getMessagesFromBus$0(Receiver.java:55)
at io.vertx.core.eventbus.impl.HandlerRegistration.handleMessage(HandlerRegistration.java:207)
at io.vertx.core.eventbus.impl.HandlerRegistration.handle(HandlerRegistration.java:201)
at io.vertx.core.eventbus.impl.EventBusImpl.lambda$deliverToHandler$127(EventBusImpl.java:498)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$18(ContextImpl.java:335)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:358)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at java.lang.Thread.run(Thread.java:745)
Receiver ->>>>>>>> Message{username=Aaaewfewf2d, message=41414wefwef2d2}
How to correctly get all messages from the receiver? Or if the bus received messages, should I immediately store them to the db? Can a message bus keep messages and not lost them?
Thanks
Each hit in the entry point "/api/messagelist" creates one new consumer with the request routing context.
The first request will create the consumer and reply to the request. When the second message was published, that consumer will receive the message and will reply to the previous request (instance) and this was closed.
I think that you misunderstood the event bus purpose and I really recommend you to read the documentation.
http://vertx.io/docs/vertx-core/java/#event_bus
I did not had the chance to test your code but it seems that the publish operation is throwing an exception and vertx will try to send back an error message. However you already replied and ended the connection.
Now the error might be from your codec but due to the asynchronous nature of vertx you only see it at a later stage and mangled with the internal error handler.
I'm new to Netty, and am looking at using it to make a simple http proxy server that receives requests from a client, forwards the requests to another server, and then copies the response back to the response for the original request. One extra requirement is that I be able to support a timeout, so that if the proxied server takes too long to respond the proxy will respond by itself and close the connection to the proxied server.
I've already implemented such an application using Jetty, but with Jetty I need to use too many threads to keep inbound requests from getting blocked (this is a lightweight app that uses very little memory or cpu, but the latency of the proxied server is high enough that bursts in traffic cause either queueing in the proxy server, or require too many threads).
According to my understanding, I can use Netty to build a pipeline in which each stage performs a small amount of computation, then releases it's thread and waits until data is ready for the next stage in the pipeline to be executed.
My question is, is there a simple example of such an application? What I have so far is a simple modification of the server code for the basic Netty tutorial, but it lacks all support for a client. I saw the netty client tutorial, but am not sure how to mix code from the two to create a simple proxy app.
public static void main(String[] args) throws Exception {
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap bootstrap = new ServerBootstrap(factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestDecoder(),
new HttpResponseEncoder(),
/*
* Is there something I can put here to make a
* request to another server asynchronously and
* copy the result to the response inside
* MySimpleChannelHandler?
*/
new MySimpleChannelHandler()
);
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.bind(new InetSocketAddress(8080));
}
private static class MySimpleChannelHandler extends SimpleChannelHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
HttpRequest request = (HttpRequest) e.getMessage();
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.setContent(request.getContent());
Channel ch = e.getChannel();
ChannelFuture f = ch.write(response);
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
Channel ch = future.getChannel();
ch.close();
}
});
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
you would have to look at LittleProxy to see how they did it as it is written on top of Netty.