org.jupiter.transport.netty.handler.ProtocolDecoder.java Source code

Java tutorial

Introduction

Here is the source code for org.jupiter.transport.netty.handler.ProtocolDecoder.java

Source

/*
 * Copyright (c) 2015 The Jupiter Project
 *
 * 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.jupiter.transport.netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import org.jupiter.common.util.Signal;
import org.jupiter.common.util.SystemClock;
import org.jupiter.common.util.SystemPropertyUtil;
import org.jupiter.transport.JProtocolHeader;
import org.jupiter.transport.exception.IoSignals;
import org.jupiter.transport.payload.JRequestBytes;
import org.jupiter.transport.payload.JResponseBytes;

import java.util.List;

/**
 * **************************************************************************************************
 *                                          Protocol
 *                                                  ?
 *       2      1       1        8           4      
 *                                                  
 *                                                  
 *    MAGIC   Sign    Status   Invoke Id   Body Length                   Body Content              
 *                                                  
 *                                                  
 *
 * ?16
 * = 2 // magic = (short) 0xbabe
 * + 1 // ??, ?4???request/response/heartbeat, ?4???
 * + 1 // ??, ??
 * + 8 // ? id, long , ?jupiter?id?48?, ?16?
 * + 4 // ? body , int 
 *
 * jupiter
 * org.jupiter.transport.netty.handler
 *
 * @author jiachun.fjc
 */
public class ProtocolDecoder extends ReplayingDecoder<ProtocolDecoder.State> {

    // ???, 5M
    private static final int MAX_BODY_SIZE = SystemPropertyUtil.getInt("jupiter.io.decoder.max.body.size",
            1024 * 1024 * 5);

    /**
     * Cumulate {@link ByteBuf}s by add them to a CompositeByteBuf and so do no memory copy whenever possible.
     * Be aware that CompositeByteBuf use a more complex indexing implementation so depending on your use-case
     * and the decoder implementation this may be slower then just use the {@link #MERGE_CUMULATOR}.
     */
    private static final boolean USE_COMPOSITE_BUF = SystemPropertyUtil
            .getBoolean("jupiter.io.decoder.composite.buf", false);

    public ProtocolDecoder() {
        super(State.HEADER_MAGIC);
        if (USE_COMPOSITE_BUF) {
            setCumulator(COMPOSITE_CUMULATOR);
        }
    }

    // ??
    private final JProtocolHeader header = new JProtocolHeader();

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        switch (state()) {
        case HEADER_MAGIC:
            checkMagic(in.readShort()); // MAGIC
            checkpoint(State.HEADER_SIGN);
        case HEADER_SIGN:
            header.sign(in.readByte()); // ??
            checkpoint(State.HEADER_STATUS);
        case HEADER_STATUS:
            header.status(in.readByte()); // ??
            checkpoint(State.HEADER_ID);
        case HEADER_ID:
            header.id(in.readLong()); // ?id
            checkpoint(State.HEADER_BODY_LENGTH);
        case HEADER_BODY_LENGTH:
            header.bodyLength(in.readInt()); // ?
            checkpoint(State.BODY);
        case BODY:
            switch (header.messageCode()) {
            case JProtocolHeader.HEARTBEAT:
                break;
            case JProtocolHeader.REQUEST: {
                int length = checkBodyLength(header.bodyLength());
                byte[] bytes = new byte[length];
                in.readBytes(bytes);

                JRequestBytes request = new JRequestBytes(header.id());
                request.timestamp(SystemClock.millisClock().now());
                request.bytes(header.serializerCode(), bytes);

                out.add(request);

                break;
            }
            case JProtocolHeader.RESPONSE: {
                int length = checkBodyLength(header.bodyLength());
                byte[] bytes = new byte[length];
                in.readBytes(bytes);

                JResponseBytes response = new JResponseBytes(header.id());
                response.status(header.status());
                response.bytes(header.serializerCode(), bytes);

                out.add(response);

                break;
            }
            default:
                throw IoSignals.ILLEGAL_SIGN;
            }
            checkpoint(State.HEADER_MAGIC);
        }
    }

    private static void checkMagic(short magic) throws Signal {
        if (magic != JProtocolHeader.MAGIC) {
            throw IoSignals.ILLEGAL_MAGIC;
        }
    }

    private static int checkBodyLength(int size) throws Signal {
        if (size > MAX_BODY_SIZE) {
            throw IoSignals.BODY_TOO_LARGE;
        }
        return size;
    }

    enum State {
        HEADER_MAGIC, HEADER_SIGN, HEADER_STATUS, HEADER_ID, HEADER_BODY_LENGTH, BODY
    }
}