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

net.scattersphere.server.handler.stream.ConnectionReadHandler Maven / Gradle / Ivy

The newest version!
/*
 * Scattersphere
 * Copyright 2014-2015, Scattersphere 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 net.scattersphere.server.handler.stream;

import net.scattersphere.data.DataSerializer;
import net.scattersphere.data.message.JobMessage;
import net.scattersphere.server.ClientConnection;
import net.scattersphere.server.handler.core.MessageHandler;
import net.scattersphere.server.handler.stream.message.StreamMessageHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vertx.java.core.Handler;
import org.vertx.java.core.buffer.Buffer;

import java.util.ArrayList;
import java.util.List;

/**
 * This is the read handler, and is called when new data is received from an endpoint in the Streaming server.
 *
 * Created by kenji on 2/8/15.
 */
public class ConnectionReadHandler implements Handler {

    private final ClientConnection client;
    private Buffer currentBuffer;

    private final List messageHandlers;

    private final Logger LOG = LoggerFactory.getLogger(ConnectionReadHandler.class);

    /**
     * Instantiates a new read handler, receiving the {@link net.scattersphere.server.ClientConnection} container object.
     *
     * @param client {@link net.scattersphere.server.ClientConnection} object.
     */
    public ConnectionReadHandler(ClientConnection client) {
        this.client = client;
        this.currentBuffer = new Buffer();
        this.messageHandlers = new ArrayList<>();

        messageHandlers.add(new StreamMessageHandler());
    }

    private void handleMessage(JobMessage message) {
        // TODO: Lambda

        for(MessageHandler handler : messageHandlers) {
            if (handler.canHandle(message)) {
                handler.handle(message, client);
                return;
            }
        }

        LOG.warn("Unknown command received from client at {}: {}", client.getEndpoint().remoteAddress(), message.getMessage());
    }

    /**
     * Handles a read request from the client.
     *
     * As data is received, the header of the message must contain the number of bytes for the packet.  The server will then
     * wait and read data from the client.  As the data is read in pieces, the packet is appended to an internal read buffer.
     * Once the buffer meets or exceeds the expected payload size, the packet is trimmed, and deserialized.
     *
     * Once deserialized as a {@link net.scattersphere.data.message.JobMessage}, it is dispatched to an appropriate message handler.  If the message was
     * malformed, or the message could not be deserialized, no response is sent to the client.  Rather, the server quietly
     * logs the error or exception, and continues reading data from the client.
     *
     * Packet sizes cannot exceed that defined in {@code ClientConnection.MAX_PAYLOAD}.  If this expected payload size is
     * exceeded, the message is dropped, and the client is abruptly disconnected.
     *
     * @param buffer A {@link org.vertx.java.core.buffer.Buffer} of data from VertX.
     */
    @Override
    public void handle(Buffer buffer) {
        byte data[] = null;

        // TODO
        // Go over this read code with a microscope.  Check to see if there's any code in VertX that will handle
        // this in a neater fashion.

        if (client.getExpectedPayloadSize() == 0) {
            int payloadSize = 0;

            if (buffer.length() > DataSerializer.PACKET_HEADER_SIZE) {
                data = buffer.getBytes();
                payloadSize = DataSerializer.getPacketSize(data);
            }

            if (payloadSize > ClientConnection.MAX_PAYLOAD) {
                LOG.debug("READ: unexpected payload size: sent={} max={}", payloadSize, ClientConnection.MAX_PAYLOAD);
                client.getEndpoint().close();
                return;
            } else {
                LOG.debug("READ: payloadSize={} appendedBufferSize={}", payloadSize, buffer.length());
            }

            byte[] newData = new byte[data.length - DataSerializer.PACKET_HEADER_SIZE];

            System.arraycopy(data, DataSerializer.PACKET_HEADER_SIZE, newData, 0, data.length - DataSerializer.PACKET_HEADER_SIZE);

            client.setExpectedPayloadSize(payloadSize);
            client.getReadBuffer().setBytes(0, newData);
        } else {
            client.getReadBuffer().appendBuffer(buffer);
        }

        data = client.getReadBuffer().getBytes();

        if (data.length >= client.getExpectedPayloadSize()) {
            int newBufferLength = (int) (data.length - client.getExpectedPayloadSize());
            byte[] newBuffer = null;

            client.clearReadBuffer();

            if (newBufferLength > 0) {
                newBuffer = new byte[newBufferLength];
                System.arraycopy(data, (int) client.getExpectedPayloadSize(), newBuffer, 0, newBufferLength);
                client.getReadBuffer().setBytes(0, newBuffer);

                LOG.debug("READ: appended read-ahead buffer: length={}", currentBuffer.length());
            }

            LOG.debug("READ: full packet received: length={}", client.getExpectedPayloadSize());

            Object dataObject = DataSerializer.deserialize(data);

            LOG.debug("Got message: {}", dataObject.getClass().getName());

            if (dataObject instanceof JobMessage) {
                JobMessage jMessage = (JobMessage) dataObject;

                LOG.debug("READ: message requested: {}", jMessage);

                handleMessage(jMessage);
            } else {
                LOG.warn("Unknown message received: " + dataObject.getClass().getName());
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy