/* * Copyright 2014-2016 CyberVision, Inc. * * 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.kaaproject.kaa.common.channels.protocols.kaatcp; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.ConnAck; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.Connect; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.Disconnect; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.KaaSync; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.MessageFactory; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.MessageType; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.MqttFrame; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.PingRequest; import org.kaaproject.kaa.common.channels.protocols.kaatcp.messages.PingResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Kaatcp Framer Class. Used to cut incoming byte stream into MQTT frames, and deliver frames to * {@link MqttFramelistener}. Framer Class typically used from {@link MessageFactory} Class. * * @author Andrey Panasenko */ public class Framer { public static final Logger LOG = LoggerFactory //NOSONAR .getLogger(Framer.class); /** * Mqtt frame listeners list. */ private final List<MqttFramelistener> listeners; /** * Current processing frame. */ private MqttFrame currentFrame; /** * Default constructor. */ public Framer() { listeners = new ArrayList<>(); } /** * Register Mqtt frame listener. * * @param listener MqttFramelistener */ public void registerFrameListener(MqttFramelistener listener) { listeners.add(listener); } /** * Process incoming bytes stream. * Assumes that bytes is unprocessed bytes. * In case of previous pushBytes() eaten not all bytes on next iterations * bytes array should starts from unprocessed bytes. * * @param bytes byte[] to push * @return number of bytes processed from this array. * @throws KaaTcpProtocolException throws in case of protocol errors. */ public int pushBytes(byte[] bytes) throws KaaTcpProtocolException { if (LOG.isTraceEnabled()) { if (bytes != null) { LOG.trace("Received bytes: {}", Arrays.toString(bytes)); } } int used = 0; while (bytes.length > used) { if (currentFrame == null) { if ((bytes.length - used) >= 1) { // 1 bytes minimum header length int intType = bytes[used] & 0xFF; currentFrame = getFrameByType((byte) (intType >> 4)); ++used; } else { break; } } used += currentFrame.push(bytes, used); if (currentFrame.decodeComplete()) { callListeners(currentFrame.upgradeFrame()); currentFrame = null; } } return used; } /** * Notify all listeners on new Frame. */ private void callListeners(MqttFrame frame) { for (MqttFramelistener listener : listeners) { listener.onMqttFrame(frame); } } /** * Creates specific Kaatcp message by MessageType. * * @param type - MessageType of mqttFrame * @return mqttFrame * @throws KaaTcpProtocolException if specified type is unsupported */ private MqttFrame getFrameByType(byte type) throws KaaTcpProtocolException { MqttFrame frame = null; if (type == MessageType.CONNACK.getType()) { frame = new ConnAck(); } else if (type == MessageType.CONNECT.getType()) { frame = new Connect(); } else if (type == MessageType.DISCONNECT.getType()) { frame = new Disconnect(); } else if (type == MessageType.KAASYNC.getType()) { frame = new KaaSync(); } else if (type == MessageType.PINGREQ.getType()) { frame = new PingRequest(); } else if (type == MessageType.PINGRESP.getType()) { frame = new PingResponse(); } else { throw new KaaTcpProtocolException("Got incorrect messageType format " + type); } return frame; } /** * Reset Framer state by dropping currentFrame. */ public void flush() { currentFrame = null; } }