jlibs.wamp4j.netty.NettyWebSocket.java Source code

Java tutorial

Introduction

Here is the source code for jlibs.wamp4j.netty.NettyWebSocket.java

Source

/**
 * Copyright 2015 Santhosh Kumar Tekuri
 *
 * The JLibs authors license this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package jlibs.wamp4j.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.nio.AbstractNioChannel;
import io.netty.handler.codec.http.websocketx.*;
import jlibs.wamp4j.spi.Listener;
import jlibs.wamp4j.spi.MessageType;
import jlibs.wamp4j.spi.WAMPOutputStream;
import jlibs.wamp4j.spi.WAMPSocket;

import java.lang.reflect.Method;
import java.nio.channels.SocketChannel;

/**
 * @author Santhosh Kumar Tekuri
 */
public class NettyWebSocket extends ChannelInboundHandlerAdapter implements WAMPSocket {
    private final WebSocketServerHandshaker handshaker;
    private final String subProtocol;
    protected ChannelHandlerContext ctx;
    private ChannelPromise voidPromise;

    public NettyWebSocket(WebSocketServerHandshaker handshaker, String subProtocol) {
        this.handshaker = handshaker;
        this.subProtocol = subProtocol;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        voidPromise = ctx.channel().voidPromise();
        if (listener != null)
            listener.readyToWrite(this);
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (listener != null)
            listener.onClose(this);
        super.channelInactive(ctx);
    }

    private final NettyInputStream is = new NettyInputStream();

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof WebSocketFrame) {
            WebSocketFrame frame = (WebSocketFrame) msg;
            try {
                if (frame instanceof TextWebSocketFrame || frame instanceof BinaryWebSocketFrame) {
                    if (listener != null) {
                        MessageType type = frame instanceof TextWebSocketFrame ? MessageType.text
                                : MessageType.binary;
                        is.reset(frame.content());
                        listener.onMessage(this, type, is);
                    }
                } else if (frame instanceof PingWebSocketFrame)
                    ctx.write(new PongWebSocketFrame(frame.content().retain()));
                else if (frame instanceof CloseWebSocketFrame)
                    handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            } finally {
                frame.release();
            }
        } else
            ctx.fireChannelRead(msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        if (listener != null)
            listener.onReadComplete(this);
        ctx.fireChannelReadComplete();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (listener != null)
            listener.onError(this, cause);
    }

    @Override
    public String subProtocol() {
        return subProtocol;
    }

    protected Listener listener;

    @Override
    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    public void send(MessageType type, WAMPOutputStream out) {
        ByteBuf buffer = ((NettyOutputStream) out).buffer;
        WebSocketFrame frame = type == MessageType.text ? new TextWebSocketFrame(buffer)
                : new BinaryWebSocketFrame(buffer);
        ctx.write(frame, voidPromise);
    }

    @Override
    public boolean isAutoRead() {
        return ctx.channel().config().isAutoRead();
    }

    @Override
    public void setAutoRead(boolean autoRead) {
        ctx.channel().config().setAutoRead(autoRead);
    }

    @Override
    public boolean isWritable() {
        return ctx.channel().isWritable();
    }

    @Override
    public void flush() {
        ctx.flush();
    }

    @Override
    public boolean isOpen() {
        return ctx.channel().isOpen();
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isWritable() && listener != null)
            listener.readyToWrite(this);
    }

    @Override
    public void close() {
        ctx.writeAndFlush(new CloseWebSocketFrame()).addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void kill() {
        try {
            Method method = AbstractNioChannel.class.getDeclaredMethod("javaChannel");
            method.setAccessible(true);
            SocketChannel channel = (SocketChannel) method.invoke(ctx.channel());
            channel.close();
        } catch (Exception ex) {
            ex.printStackTrace();
            ctx.close();
        }
    }
}