All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fisco.bcos.sdk.channel.ChannelMsgHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2020  [fisco-dev]
 *
 * Licensed 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 org.fisco.bcos.sdk.channel;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.fisco.bcos.sdk.channel.model.ChannelHandshake;
import org.fisco.bcos.sdk.channel.model.ChannelMessageError;
import org.fisco.bcos.sdk.channel.model.ChannelPrococolExceiption;
import org.fisco.bcos.sdk.channel.model.ChannelProtocol;
import org.fisco.bcos.sdk.channel.model.ChannelRequest;
import org.fisco.bcos.sdk.channel.model.EnumChannelProtocolVersion;
import org.fisco.bcos.sdk.channel.model.EnumNodeVersion;
import org.fisco.bcos.sdk.channel.model.EnumSocketChannelAttributeKey;
import org.fisco.bcos.sdk.model.Message;
import org.fisco.bcos.sdk.model.MsgType;
import org.fisco.bcos.sdk.model.NodeVersion;
import org.fisco.bcos.sdk.model.Response;
import org.fisco.bcos.sdk.network.MsgHandler;
import org.fisco.bcos.sdk.utils.ChannelUtils;
import org.fisco.bcos.sdk.utils.ObjectMapperFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An implementation of channel.
 *
 * @author chaychen
 */
public class ChannelMsgHandler implements MsgHandler {

    private static Logger logger = LoggerFactory.getLogger(ChannelImp.class);
    private final ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();

    private List msgConnectHandlerList = new CopyOnWriteArrayList();
    private List msgDisconnectHandleList = new CopyOnWriteArrayList();
    private Map msgHandlers = new ConcurrentHashMap<>();
    private List msgEstablishHandlerList = new CopyOnWriteArrayList();

    private Map seq2Callback = new ConcurrentHashMap<>();
    private Map availablePeer = new ConcurrentHashMap<>();

    public Map getAvailablePeer() {
        return availablePeer;
    }

    public void addConnectHandler(MsgHandler handler) {
        msgConnectHandlerList.add(handler);
    }

    public void addEstablishHandler(MsgHandler handler) {
        msgEstablishHandlerList.add(handler);
    }

    public void addMessageHandler(MsgType type, MsgHandler handler) {
        msgHandlers.put(type.getType(), handler);
    }

    public void addDisconnectHandler(MsgHandler handler) {
        msgDisconnectHandleList.add(handler);
    }

    public void addSeq2CallBack(String seq, ResponseCallback callback) {
        seq2Callback.put(seq, callback);
    }

    public void removeSeq(String seq) {
        seq2Callback.remove(seq);
    }

    private void addAvailablePeer(String host, ChannelHandlerContext ctx) {
        availablePeer.put(host, ctx);
        for (MsgHandler msgHandler : msgEstablishHandlerList) {
            msgHandler.onConnect(ctx);
        }
    }

    private void removeAvailablePeers(String host) {
        if (availablePeer.containsKey(host)) {
            availablePeer.remove(host);
        }
    }

    private ResponseCallback getAndRemoveSeq(String seq) {
        ResponseCallback callback = seq2Callback.get(seq);
        seq2Callback.remove(seq);
        return callback;
    }

    @Override
    public void onConnect(ChannelHandlerContext ctx) {
        logger.debug(
                "onConnect in ChannelMsgHandler called, host : {}",
                ChannelVersionNegotiation.getPeerHost(ctx));
        queryNodeVersion(ctx);
        for (MsgHandler msgHandler : msgConnectHandlerList) {
            msgHandler.onConnect(ctx);
        }
    }

    @Override
    public void onMessage(ChannelHandlerContext ctx, Message msg) {
        logger.debug(
                "onMessage in ChannelMsgHandler called, host : {}, seq : {}, msgType : {}",
                ChannelVersionNegotiation.getPeerHost(ctx),
                msg.getSeq(),
                (int) msg.getType());
        ResponseCallback callback = getAndRemoveSeq(msg.getSeq());
        if (callback != null) {
            callback.cancelTimeout();
            logger.trace(
                    " call registered callback, seq: {}, type: {} ,result: {}",
                    msg.getSeq(),
                    msg.getType(),
                    msg.getResult());

            Response response = new Response();
            if (msg.getResult() != 0) {
                response.setErrorMessage("Response error");
            }
            response.setErrorCode(msg.getResult());
            response.setMessageID(msg.getSeq());
            response.setContentBytes(msg.getData());
            response.setCtx(ctx);
            callback.onResponse(response);
        } else {
            MsgHandler msgHandler = msgHandlers.get(msg.getType().intValue());
            if (msgHandler != null) {
                logger.trace(
                        " receive message, no callback, call handler, seq:{} , type: {}, result: {}",
                        msg.getSeq(),
                        (int) msg.getType(),
                        msg.getResult());
                msgHandler.onMessage(ctx, msg);
            }
            logger.debug(" call ");
        }
    }

    @Override
    public void onDisconnect(ChannelHandlerContext ctx) {
        String host = ChannelVersionNegotiation.getPeerHost(ctx);
        logger.debug("onDisconnect in ChannelMsgHandler called, host : {}", host);
        for (MsgHandler handle : msgDisconnectHandleList) {
            handle.onDisconnect(ctx);
        }
        removeAvailablePeers(host);
    }

    private void queryNodeVersion(ChannelHandlerContext ctx) {
        ChannelRequest request = new ChannelRequest("getClientVersion", Arrays.asList());
        String seq = ChannelUtils.newSeq();
        Message message = new Message();
        try {
            byte[] payload = objectMapper.writeValueAsBytes(request);
            message.setSeq(seq);
            message.setResult(0);
            message.setType((short) MsgType.CHANNEL_RPC_REQUEST.getType());
            message.setData(payload);
            logger.trace(
                    "encodeRequestToMessage, seq: {}, method: {}, messageType: {}",
                    message.getSeq(),
                    request.getMethod(),
                    message.getType());
        } catch (JsonProcessingException e) {
            logger.error(
                    "encodeRequestToMessage failed for decode the message exception, errorMessage: {}",
                    e.getMessage());
        }

        ResponseCallback callback =
                new ResponseCallback() {
                    @Override
                    public void onResponse(Response response) {
                        Boolean disconnect = true;
                        try {
                            if (response.getErrorCode()
                                    == ChannelMessageError.MESSAGE_TIMEOUT.getError()) {
                                // The node version number is below 2.1.0 when request timeout
                                ChannelVersionNegotiation.setProtocolVersion(
                                        ctx,
                                        EnumChannelProtocolVersion.VERSION_1,
                                        "below-2.1.0-timeout");

                                logger.info(
                                        " query node version timeout, content: {}",
                                        response.getContent());
                                return;
                            } else if (response.getErrorCode() != 0) {
                                logger.error(
                                        " node version response, code: {}, message: {}",
                                        response.getErrorCode(),
                                        response.getErrorMessage());

                                throw new ChannelPrococolExceiption(
                                        " query node version failed, code: "
                                                + response.getErrorCode()
                                                + ", message: "
                                                + response.getErrorMessage());
                            }

                            NodeVersion nodeVersion =
                                    objectMapper.readValue(
                                            response.getContent(), NodeVersion.class);
                            logger.info(
                                    " node: {}, content: {}",
                                    nodeVersion.getResult(),
                                    response.getContent());

                            if (EnumNodeVersion.channelProtocolHandleShakeSupport(
                                    nodeVersion.getResult().getSupportedVersion())) {
                                // node support channel protocol handshake, start it
                                logger.info(" support channel handshake node");
                                queryChannelProtocolVersion(ctx);
                            } else { // default channel protocol
                                logger.info(" not support channel handshake set default");
                                ChannelVersionNegotiation.setProtocolVersion(
                                        ctx,
                                        EnumChannelProtocolVersion.VERSION_1,
                                        nodeVersion.getResult().getSupportedVersion());
                                addPeerHost(ctx);
                            }
                            disconnect = false;
                        } catch (Exception e) {
                            logger.error(" query node version failed, message: {}", e.getMessage());
                        }

                        if (disconnect) {
                            ctx.disconnect();
                            ctx.close();
                        }
                    }
                };

        ctx.writeAndFlush(message);
        addSeq2CallBack(seq, callback);
    }

    private void queryChannelProtocolVersion(ChannelHandlerContext ctx)
            throws ChannelPrococolExceiption {
        final String host = ChannelVersionNegotiation.getPeerHost(ctx);
        String seq = ChannelUtils.newSeq();
        Message message = new Message();

        try {
            ChannelHandshake channelHandshake = new ChannelHandshake();
            byte[] payload = objectMapper.writeValueAsBytes(channelHandshake);
            message.setSeq(seq);
            message.setResult(0);
            message.setType(Short.valueOf((short) MsgType.CLIENT_HANDSHAKE.getType()));
            message.setData(payload);
            logger.trace(
                    "encodeChannelHandshakeToMessage, seq: {}, data: {}, messageType: {}",
                    message.getSeq(),
                    channelHandshake.toString(),
                    message.getType());
        } catch (JsonProcessingException e) {
            logger.error(
                    "queryChannelProtocolVersion failed for decode the message exception, errorMessage: {}",
                    e.getMessage());
            throw new ChannelPrococolExceiption(e.getMessage());
        }

        ResponseCallback callback =
                new ResponseCallback() {
                    @Override
                    public void onResponse(Response response) {
                        Boolean disconnect = true;
                        try {
                            if (response.getErrorCode() != 0) {
                                logger.error(
                                        " channel protocol handshake request failed, code: {}, message: {}",
                                        response.getErrorCode(),
                                        response.getErrorMessage());
                                throw new ChannelPrococolExceiption(
                                        " channel protocol handshake request failed, code: "
                                                + response.getErrorCode()
                                                + ", message: "
                                                + response.getErrorMessage());
                            }

                            ChannelProtocol channelProtocol =
                                    objectMapper.readValue(
                                            response.getContent(), ChannelProtocol.class);
                            EnumChannelProtocolVersion enumChannelProtocolVersion =
                                    EnumChannelProtocolVersion.toEnum(
                                            channelProtocol.getProtocol());
                            channelProtocol.setEnumProtocol(enumChannelProtocolVersion);
                            logger.info(
                                    " channel protocol handshake success, set socket channel protocol, host: {}, channel protocol: {}",
                                    host,
                                    channelProtocol);

                            ctx.channel()
                                    .attr(
                                            AttributeKey.valueOf(
                                                    EnumSocketChannelAttributeKey
                                                            .CHANNEL_PROTOCOL_KEY.getKey()))
                                    .set(channelProtocol);
                            disconnect = false;
                        } catch (Exception e) {
                            logger.error(
                                    " channel protocol handshake failed, exception: {}",
                                    e.getMessage());
                        }
                        if (disconnect) {
                            ctx.disconnect();
                            ctx.close();
                        } else {
                            addPeerHost(ctx);
                        }
                    }
                };

        ctx.writeAndFlush(message);
        addSeq2CallBack(seq, callback);
    }

    private void addPeerHost(ChannelHandlerContext ctx) {
        String host = ChannelVersionNegotiation.getPeerHost(ctx);
        addAvailablePeer(host, ctx);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy