Play Framework 2: Handle client disconnection in chunked response - java

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);
}
}
}
}
}

Related

Netty channel traffic shaping issue : channelWritabilityChanged method not trigger

I want to control bandwidth for data transfer.
According to Netty document, they suggest:
In your handler, you should consider to use the channel.isWritable() and channelWritabilityChanged(ctx) to handle writability, or through future.addListener(new GenericFutureListener()) on the future returned by ctx.write().
Here is my channel initializer source code:
public class MyChannelInitializer extends ChannelInitializer<Channel>
{
private int mode;
private Server server;
private String fileName;
public MyChannelInitializer(Server server, int mode,String fileName)
{
this.mode=mode;
this.server=server;
this.fileName=fileName;
}
#Override
protected void initChannel(Channel ch) throws Exception
{
if (this.mode==MyFtpServer.RECEIVEFILE)
{
ch.pipeline().addLast(new ChannelTrafficShapingHandler(0L,10240L));
ch.pipeline().addLast(new ReceiveFileHandler(this.fileName,server));
}
else
{
ch.pipeline().addLast(new ChannelTrafficShapingHandler(10240L,0L));
ch.pipeline().addLast("streamer", new ChunkedWriteHandler());
ch.pipeline().addLast("handler",new SendFileHandler(this.fileName,server));
}
}
}
Here is Send File Handler source code:
public class SendFileHandler extends SimpleChannelInboundHandler<ByteBuf>
{
String fileName;
PassiveServer txServer=null;
public SendFileHandler(String fileName, PassiveServer txServer)
{
this.fileName=fileName;
this.txServer=txServer;
}
public void channelWritabilityChanged(ChannelHandlerContext ctx)throws IOException
{
System.out.println("isWritable="+ctx.channel().isWritable());
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws IOException
{
System.out.println("SendFileHandler active");
}
}
Would you tell me why "channelWritabilityChanged" method in my SendFileHandler is never trigger, only channelActive method is triggered.?
After I changed SendFileHandler.channelActive method as the following:
public void channelActive(ChannelHandlerContext ctx) throws IOException
{
Calendar startTime;
System.out.println("SendFileHandler active");
startTime=Calendar.getInstance();
ctx.fireChannelWritabilityChanged();
}
The SendFileHandler.channelWritabilityChanged method still not triggered.
Would anyone tell me why?
I believe because ChunkedWriteHandler does the job, taking into account writability directly. However, since its last code in channelWritabilityChanged is:
ctx.fireChannelWritabilityChanged();
You should have the signal of writability changed.
But do you send any file? Because in your code, not, and therefore no channelWritabilityChanged shall be raized since there seems to be no write at all... (of course, writability only occurs on write part, not on read part).
You could also have a look at this thread regarding traffic shaping.

Exception handling in Spring XD streams

How can you create a failsafe Spring XD stream, which will keep running properly after an exception is triggered for one specific message (i.e.logs the error but continues consuming the next messages in the stream), without having to add try catch(Throwable) in every Stream step?
Is there any easy way of doing this with the Reactor or RxJava model?
Example stream using Reactor:
#Override
public Publisher<Tuple> process(Stream<GenericMessage> inputStream) {
return inputStream
.flatMap(SomeClass::someFlatMap)
.filter(SomeClass::someFilter)
.when(Throwable.class, t -> log.error("error", t));
}
RxJava can be used by a processor module. On creation the subscription needs to be created and to handle errors the subscriber needs to add an onError handler:
subject = new SerializedSubject(PublishSubject.create());
Observable<?> outputStream = processor.process(subject);
subscription = outputStream.subscribe(new Action1<Object>() {
#Override
public void call(Object outputObject) {
if (ClassUtils.isAssignable(Message.class, outputObject.getClass())) {
getOutputChannel().send((Message) outputObject);
} else {
getOutputChannel().send(MessageBuilder.withPayload(outputObject).build());
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
logger.error(throwable.getMessage(), throwable);
}
}, new Action0() {
#Override
public void call() {
logger.error("Subscription close for [" + subscription + "]");
}
});
Look at more examples here: https://github.com/spring-projects/spring-xd/tree/master/spring-xd-rxjava/src

Creating SockJS server with Java

I'm very new with Vert.x so excuse my newbness.
I was able to create a very simply SockJS server with Vert.x however I can't figure out how to register events/callbacks/handlers when connections are open or closed.
With JSR-356, it's drop dead simple to handle open/close connection events:
#OnOpen
public void onOpen(Session userSession) {
// Do whatever you need
}
#OnClose
public void onClose(Session userSession) {
// Do whatever you need
}
Using the SockJS support in Spring Framework 4.0 M1+, it's almost the same as JSR-356:
public class MySockJsServer extends TextWebSocketHandlerAdapter {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// Do whatever you need
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// Do whatever you need
}
}
For some reason I couldn't figure out how to do something so conceptually simple in Vert.x. I though Vert.x was simple ?!!
If anyone can point me in the right direction, please help.
I played around with EventBus and EventBus hooks but it didn't work. Perhaps that's the wrong approach anyhow.
I'm using Vert.x version 2.0.1
TIA
This is the answer:
HttpServer httpServer = vertx.createHttpServer();
// Create HTTP server
httpServer = httpServer.requestHandler(new Handler<HttpServerRequest>() {
#Override
public void handle(HttpServerRequest req) {
req.response().sendFile("web/" + req.path());
}
});
// Create SockJS Server
SockJSServer sockJSServer = vertx.createSockJSServer(httpServer);
sockJSServer = sockJSServer.installApp(new JsonObject().putString("prefix", "/test"), new Handler<SockJSSocket>() {
public void handle(final SockJSSocket sock) {
System.out.println("New session detected!");
// Message handler
sock.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
System.out.println("In dataHandler");
}
});
// Session end handler
sock.endHandler(new Handler<Void>() {
#Override
public void handle(Void arg) {
System.out.println("In endHandler");
}
});
}
});
httpServer.listen(8080);

tomcat websocket servlet listening port

I'm trying to write a servlet that uses org.apache.catalina.websocket.WebSocketServlet. I've found an example of websocket chat, but I can't figure out how can I specify the listening port for a websocket server (which is implemented in this servlet)? For example I need to listen for clients connection on port 11337. But how I can express this?
OK, here is the (simplified) code:
public class TestServlet extends WebSocketServlet {
private static final Logger logger = LoggerFactory.getLogger(TestServlet.class);
public TestServlet() {
logger.error("Initializing TestServlet");
}
#Override
protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
logger.error("New WS connection, subProtocol=" + subProtocol + ", request=" + request.getRequestURL());
return new TestConnection();
}
private class TestConnection extends MessageInbound {
#Override
protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
logger.error("onBinaryMessage");
}
#Override
protected void onTextMessage(CharBuffer charBuffer) throws IOException {
logger.error("onBinaryMessage: " + charBuffer);
sendMessage("Test message");
}
public void sendMessage(String message) {
WsOutbound outbound = this.getWsOutbound();
CharBuffer cb = CharBuffer.wrap(message);
try {
outbound.writeTextMessage(cb);
} catch (IOException e) {
logger.error("failed to write outbound");
}
}
}
}
I can't find where and how I can set listening port. Official websocket documentation also doesn't help much.
So I guess it can be set somewhere in servlet settings, but can't find where.
Does anyone have any ideas?
WebSocket is designed to work over HTTP protocol and so it won't have different listening port like normal TCP Socket but it use the same server port that it deployed on.
so here in your case, if you are using tomcat server than websocket may use port 8080 (if you haven't modified explicitly) for communication. You may need to override onOpen and onClose methods of MessageInbound class to get notified on Connection established and connection close. Refer below sample code for more details.
public class IncomingMessageHandler extends MessageInbound {
private WsOutbound myoutbound;
public IncomingMessageHandler() {
}
#Override
public void onOpen(WsOutbound outbound) {
logger.info("Open Client.");
this.myoutbound = outbound;
}
#Override
public void onClose(int status) {
logger.info("Close Client.");
}
/**
* Called when received plain Text Message
*/
#Override
public void onTextMessage(CharBuffer cb) throws IOException {
}
/**
* We can use this method to pass image binary data, eventually !
*/
#Override
public void onBinaryMessage(ByteBuffer bb) throws IOException {
}
public synchronized void sendTextMessage(String message) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
this.getMyoutbound().writeTextMessage(buffer);
this.getMyoutbound().flush();
} catch (IOException e) {
}
}
/**
* Set timeout in milliseconds, -1 means never
*/
#Override
public int getReadTimeout() {
return -1;
}
public WsOutbound getMyoutbound() {
return myoutbound;
}
public void setMyoutbound(WsOutbound myoutbound) {
this.myoutbound = myoutbound;
}
}

Sending output to websocket in play 2.0

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>.

Categories

Resources