Sending output to websocket in play 2.0 - java

I am trying to figure out a way to get WebSocket.Out to fire out some data when an WebSocket.In's onMessage is called.
For example:
public class Application extends Controller {
public static Result index() {
return ok(index.render("Your new application is ready."));
}
public static WebSocket<String> sockHandler() {
return new WebSocket<String>() {
public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
in.onMessage(new F.Callback<String>() {
// FIRE SOME DATA BACK OUT TO BROWSER HERE!!!
public void invoke(String event) {
Logger.info(event);
}
});
out.write("I'm contacting you regarding your recent websocket.");
}
};
}
private static void send(WebSocket.Out<String> out, String data){
out.write(data);
}
}
Any help is much appreciated.

Please check the code of the websocket-chat sample application. It will give you a model to copy for websocket management.
For example, this code:
// Send a Json event to all members
public void notifyAll(String kind, String user, String text) {
for(WebSocket.Out<JsonNode> channel: members.values()) {
ObjectNode event = Json.newObject();
event.put("kind", kind);
event.put("user", user);
event.put("message", text);
ArrayNode m = event.putArray("members");
for(String u: members.keySet()) {
m.add(u);
}
channel.write(event);
}
}
writes some data into channel, which is a WebSocket.Out<JsonNode>.

Related

Resend messages after timeout

I have a list of objects that I put in Spring AMQP. Objects come from the controller. There is a service that processes these objects. And this service may crash with an OutOfMemoryException. Therefore, I run several instances of the application.
There is a problem: when the service crashes, I lose the received messages. I read about NACK. And could use it in case of Exception or RuntimeException. But my service crashes in Error. Therefore, I cannot send NACK. Is it possible to set a timeout in AMQP, after which I would be sent a message again if I had not confirmed the messages that had arrived earlier?
Here is the code I wrote:
public class Exchanges {
public static final String EXC_RENDER_NAME = "render.exchange.topic";
public static final TopicExchange EXC_RENDER = new TopicExchange(EXC_RENDER_NAME, true, false);
}
public class Queues {
public static final String RENDER_NAME = "render.queue.topic";
public static final Queue RENDER = new Queue(RENDER_NAME);
}
#RequiredArgsConstructor
#Service
public class RenderRabbitEventListener extends RabbitEventListener {
private final ApplicationEventPublisher eventPublisher;
#RabbitListener(bindings = #QueueBinding(value = #Queue(Queues.RENDER_NAME),
exchange = #Exchange(value = Exchanges.EXC_RENDER_NAME, type = "topic"),
key = "render.#")
)
public void onMessage(Message message, Channel channel) {
String routingKey = parseRoutingKey(message);
log.debug(String.format("Event %s", routingKey));
RenderQueueObject queueObject = parseRender(message, RenderQueueObject.class);
handleMessage(queueObject);
}
public void handleMessage(RenderQueueObject render) {
GenericSpringEvent<RenderQueueObject> springEvent = new GenericSpringEvent<>(render);
springEvent.setRender(true);
eventPublisher.publishEvent(springEvent);
}
}
And this is the method that sends messages:
    #Async ("threadPoolTaskExecutor")
    #EventListener (condition = "# event.queue")
    public void start (GenericSpringEvent <RenderQueueObject> event) {
        RenderQueueObject renderQueueObject = event.getWhat ();
        send (RENDER_NAME, renderQueueObject);
}
private void send(String routingKey, Object queue) {
try {
rabbitTemplate.convertAndSend(routingKey, objectMapper.writeValueAsString(queue));
} catch (JsonProcessingException e) {
log.warn("Can't send event!", e);
}
}
You need to close the connection to get the message re-queued.
It's best to terminate the application after an OOME (which, of course, will close the connection).

When will event produced on server with MediaType.TEXT_EVENT_STREAM delivered to subscriber on client

I created an example client/server application to get familiar with Spring Webflux/Reactor Netty. Now I am a little bit confused about the behaviour on the client side when the response contains a Flux and the media type is "text/event-stream". What I could see is that each element produced on the server is sent immediately to the client but not yet delivered to the subscriber. The first delivery to the subscriber happens after the producer of the server side has completed the Flux.
This means for me that all the elements are first collected somewhere in reactor-netty on the client side until it gets a complete/error event.
Are my conclusions true or can i do something wrong there?
If it is true, will this be changed in the near future? With my currently observed behaviour most of the benefits using Spring Webflux are negated because as with Spring Mvc the consumer has to wait until the whole element collection has been created and transfered before he can start working on the elements.
My server app is:`
#SpringBootApplication
public class ServerApp {
public static void main(String[] args) {
new SpringApplicationBuilder().sources(ServerApp.class).run(args);
}
#RestController
public static class TestController {
#GetMapping(value = "/test", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> testFlux() {
class AsyncSink implements Consumer<SynchronousSink<String>> {
private List<String> allStrings = List.of(
"Hello Flux1!",
"Hello Flux2!",
"Hello Flux3!",
"Hello Flux4!",
"Hello Flux5!");
private int index = 0;
#Override
public void accept(SynchronousSink<String> sink) {
if (index == allStrings.size()) {
sink.complete();
}
else {
sink.next(allStrings.get(index++));
}
}
}
return Flux.generate(new AsyncSink());
}
}
}
and my client app is:
#SpringBootApplication
public class ClientApp {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext aContext = new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ClientApp.class).run(args);
Flux<String> aTestFlux = aContext.getBean(TestProxy.class).getFlux();
aTestFlux.subscribe(new TestSubscriber());
System.out.println("Press ENTER to exit.");
System.in.read();
}
#Bean
public WebClient webClient() {
return WebClient.builder().baseUrl("http://localhost:8080").build();
}
#Component
public static class TestProxy {
#Autowired
private WebClient webClient;
public Flux<String> getFlux() {
return webClient.get().uri("/test").accept(MediaType.TEXT_EVENT_STREAM).exchange().flatMapMany(theResponse -> theResponse.bodyToFlux(String.class));
}
}
private static class TestSubscriber extends BaseSubscriber<String> {
#Override
public void hookOnSubscribe(Subscription subscription) {
System.out.println("Subscribed");
request(Long.MAX_VALUE);
}
#Override
public void hookOnNext(String theValue) {
System.out.println(" - " + theValue);
request(1);
}
#Override
protected void hookOnComplete() {
System.out.println(" done");
}
#Override
protected void hookOnCancel() {
System.out.println(" cancelled");
}
#Override
protected void hookOnError(Throwable theThrowable) {
theThrowable.printStackTrace(System.err);
}
}
}
When i access the url http://localhost:8080/test with chrome browser i see:
data:Hello Flux1!
data:Hello Flux2!
data:Hello Flux3!
data:Hello Flux4!
data:Hello Flux5!
which for me looks like 5 http event have been sent.
Taken from the reactive documentation and rewritten to fit your need.
My guess is that in your example you have given you pass the generate function a consumer that when finished will be emitted.
By instead using the method Flux#generate(Callable<S> stateSupplier, BiFunction<S,SynchronousSink<T>,S> generator) you supply a state that will contain the items you want emitted, then in the supplied BiFunction you emit each item, one by one.
Flux<String> flux = Flux.generate(
() -> List.of("1!", "2!", "3!", "4!", "5!"),
(state, sink) -> {
if (index == allStrings.size()) {
sink.complete();
} else {
sink.next(state.get(index++));
}
});
I have not tested the code, written on mobile.

Retrieve the observed value from Californium CoAP client

I am trying to implement a CoAP client based on Californium. I make this client observing to a resource:
public static class CoapCl{
double val = 0;
CoapClient client = new CoapClient("coap://localhost/Ultrasonic");
CoapObserveRelation relation = client.observe(new CoapHandler() {
#Override public void onLoad(CoapResponse response)
{
val = Double.parseDouble(response.getResponseText());
}
#Override
public void onError() {
System.out.println("Failed");
}
});
}
I want to access the value "val" from another class. How can I do it ? I tried to call a reference from the CoapCl class like this and print the value out:
CoapCl client = new CoapCl();
while(true)
{
System.out.println("Testing: " + client.val);
}
This will print all the value I get from the CoAP client, both changed and unchanged value. What should I do if I only want to get the changed value ?
Well, the issue itself isn't related to Californium and CoAP.
Except that CoapHandler is async but this is rather a strench.
Nevertheless, I'd recommend to end up with some kind of callback:
public class CoapCl {
private final Consumer<Double> valueChangedAction;
private final CoapClient client = new CoapClient("coap://localhost/Ultrasonic");
public CoapCl(Consumer<Double> valueChangedAction) {
this.valueChangedAction = valueChangedAction;
}
public void run() {
client.observe(new CoapHandler() {
#Override
public void onLoad(CoapResponse response) {
valueChangedAction.accept(
Double.parseDouble(
response.getResponseText()
)
);
}
#Override
public void onError() {
System.out.println("Failed");
}
});
}
}
new CoapCl(val -> System.out.println("Testing: " + val)).run();
Please keep in mind you have to block the main thread someway to keep the program from immediate exit.
Before, you had blocked it with your infinite loop.
Now you'll have to use System.in.read() or Thread.sleep or something else if you have no such stuff yet in your program.

Adding new resources dynamically - CoAP

Once CoAP server is started I need to add new resources dynamically. But I have to stop and start the server again in order to access new resources. I suppose adding new resources same as adding a new HTTP servlet into already started HTTP server.
Here I added source code which is used for adding dynamic resources. If I am missing anything here let me know.
private static CoapServer server;
public CoAPEventAdapter(InputEventAdapterConfiguration eventAdapterConfiguration,
Map<String, String> globalProperties) {
this.eventAdapterConfiguration = eventAdapterConfiguration;
this.globalProperties = globalProperties;
if(server == null){
server = new CoapServer();
server.start();
}
}
#Override
public void connect() {
registerDynamicEndpoint(eventAdapterConfiguration.getName());
isConnected = true;
}
private void registerDynamicEndpoint(String adapterName) {
server.stop();
server.add(new HelloWorldResource(adapterName));
server.start();
}
class HelloWorldResource extends CoapResource {
public HelloWorldResource(String resourceName) {
// set resource identifier
super(resourceName);
// set display name
getAttributes().setTitle("Hello-World Resource");
}
#Override
public void handleGET(CoapExchange exchange) {
// respond to the request
exchange.respond("Hello World!");
}
}
I've just tried to add a CoapResource dynamically:
#Override
public void handleGET(CoapExchange exchange) {
server.getRoot().add(new CoapResource("dynstatus") {
#Override
public void handleGET(CoapExchange exchange) {
System.err.println("dynstatus!!!");
}
});
And was able to call it.

Play Framework 2: Handle client disconnection in chunked response

I'm forwarding some real-time data to web clients using chunked encoding.
In the following way, I could make HTTP streaming responses,
public Result action() {
return ok(new StringChunks() {
public void onReady(final Out<String> out) {
openStream().onData(new Consumer<String>() {
public void accept(String string) {
out.write(string);
}
}
}
}
}
But I need to clean up some resources after the client has disconnected. (by e.g. closing the browser window or when the stream reaches EOF)
When using WebSocket I could detect client disconnection using Iteratee.mapDone.
Is there an equivalent method to detect it when using Chunks?
Thanks
Well, just figured it out.
Results.Chunked.Out<A> object has onDisconnected(Callback0) method that I can register a disconnection callback. so
public Result action() {
return ok(new StringChunks() {
public void onReady(final Out<String> out) {
out.onDisconnected(new F.Callback0() {
public void invoke() throws Throwable {
// clean up things ...
}
}
openStream().onData(new Consumer<String>() {
public void accept(String string) {
out.write(string);
}
}
}
}
}

Categories

Resources