Skip to content

base upon APT(Annotation processor tookit) technolegy and freemarker template engine. on the basic of user-defined packet entity and codec interface, generate the codec interface impl automatically in compile stage.

Notifications You must be signed in to change notification settings

BOFA1ex/protocol-codec-apt4j

Repository files navigation

commons-apt4j

ProtocolProcessor

配合@ByteBufValidation, @ByteBufConvert, @Protocol, @CacheMapping等注解

基于模板注解设计模式以及Javac提供的Annotation-processor-util技术(编译前类文件的语法树处理), 实现协议的Decode/Encode

以下是引用protocol-codec-manager项目的mqtt3.0#connect报文示例

@Data
@ByteBufValidation(
        validate = @ByteBufValidation.Validate(
                index = @ByteBufInternalPoint(step = "1"),
                length = @ByteBufInternalPoint(step = "4")
        ),
        mapper = @ByteBufValidation.Mapper(
                index = @ByteBufInternalPoint(step = "1"),
                length = @ByteBufInternalPoint(step = "4")
        ),
        validateMethod = MqttPacketLengthValidateMethod.class
)
public abstract class AbstractMqttCommand {

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"0", "4"}
    )
    private Integer packetType;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"4", "7"}
    )
    private Integer packetTypeSign;

    /** 这里直接传默认最大长度4字节 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "4"),
            convertMethod = MqttPacketLengthConvertMethod.class
    )
    private Integer packetLength = 0xffffff7f;
}
---------------------------------- ----------------------------------
@Data
@EqualsAndHashCode(callSuper = true)
@CacheMapping("mqttConnectCommand")
public class MqttConnectCommand extends AbstractMqttCommand {

    /* ******************************** 可变报文头部 固定10字节 ********************************/

    /** 协议名不正确的情况下, 不处理connect报文, 防火墙可以根据该字段来识别mqtt流量 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "2"),
            length = @ByteBufInternalPoint(step = "4"),
            convertMethod = Utf8ConvertMethod.class
    )
    private String protocolName = "MQTT";

    /** 如果不支持该协议级别, 在connAck报文中响应返回码0x01, 并断开客户端连接 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = IntegerConvertMethod.class
    )
    private Integer protocolLevel = 4;

    /** 如果connect控制报文的保留标志位不为0, 直接断开客户端连接 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"7", "8"}
    )
    private Integer reserved = 0;

    /** 清理会话, 指定了会话状态的处理方式, 这个标志位控制会话状态的生存时间 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"6", "7"}
    )
    private Integer cleanSession;

    /**
     * 遗嘱标志, 如果设置为1, 表示如果连接请求被接受了,willQoS消息必须存储在服务端并且与这个网络连接关联.
     * 并且在网络连接关闭时, 服务端必须发布这个遗嘱信息, 除非服务端收到disconnect报文并删除了这个遗嘱信息
     */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"5", "6"}
    )
    private Integer willFlag;

    /** 遗嘱QoS */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"3", "5"}
    )
    private Integer willQoS;

    /** 遗嘱保留 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"2", "3"}
    )
    private Integer willRetain;

    /** 密码标志 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"1", "2"}
    )
    private Integer passwordFlag;

    /** 用户名标志 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "-1"),
            length = @ByteBufInternalPoint(step = "1"),
            convertMethod = BinaryIntegerConvertMethod.class,
            parameters = {"0", "1"}
    )
    private Integer userNameFlag;

    /** 保持连接时间 */
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "2"),
            convertMethod = IntegerConvertMethod.class
    )
    private Integer keepAliveTime;

    /* ******************************** PayLoad ********************************/
    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "2"),
            convertMethod = IntegerConvertMethod.class
    )
    private Integer clientIdLength;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "clientIdLength", keyClazz = MqttConnectCommand.class),
                    type = ByteBufInternalPoint.StepType.MODEL
            ),
            convertMethod = Utf8ConvertMethod.class
    )
    private String clientId;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "2"),
            convertMethod = IntegerConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "willFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private Integer willTopicLength;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "willTopicLength", keyClazz = MqttConnectCommand.class),
                    type = ByteBufInternalPoint.StepType.MODEL
            ),
            convertMethod = Utf8ConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "willFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private String willTopic;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "2"),
            convertMethod = IntegerConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "userNameFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private Integer useNameLength;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "useNameLength", keyClazz = MqttConnectCommand.class),
                    type = ByteBufInternalPoint.StepType.MODEL
            ),
            convertMethod = Utf8ConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "userNameFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private String userName;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(step = "2"),
            convertMethod = IntegerConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "passwordFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private Integer passwordLength;

    @ByteBufConvert(
            index = @ByteBufInternalPoint(step = "0"),
            length = @ByteBufInternalPoint(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "passwordLength", keyClazz = MqttConnectCommand.class),
                    type = ByteBufInternalPoint.StepType.MODEL
            ),
            convertMethod = Utf8ConvertMethod.class,
            condition = @ByteBufInternalCondition(model = @ByteBufInternalModel(
                    key = "mqttConnectCommand", prop = "passwordFlag", keyClazz = MqttConnectCommand.class),
                    compareValue = "1"
            )
    )
    private String password;
}
@Protocol(implName = "MqttParserImpl")
public interface MqttParser {

    @ByteBufEncode(initialCapacity = 2 << 7)
    ByteBuf encode(MqttConnectCommand command, Channel channel);

    @ByteBufDecode()
    MqttConnectCommand decode(ByteBuf buffer, Channel channel);
}

这里说明以下不使用JavaPoet依赖的原因, 当模板内容过于复杂(嵌套, 多重策略等), 建议还是模板与代码分离, 便于阅读和维护. 这里内部使用freemarker作为模板依赖, 参考了mapStruct的源码. 更多详情参考protocol-codec-manager项目...

之前有想法对guava包的eventbus做apt改造的, 但经思考本地订阅策略并不实用, 还需要维护对应的topic, 就没继续开发了.

有好的想法或者建议, 可以提issue, 多谢.

About

base upon APT(Annotation processor tookit) technolegy and freemarker template engine. on the basic of user-defined packet entity and codec interface, generate the codec interface impl automatically in compile stage.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published