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

org.coos.messaging.transport.NioTCPTransport Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
/**
 * COOS - Connected Objects Operating System (www.connectedobjects.org).
 *
 * Copyright (C) 2009 Telenor ASA and Tellu AS. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 *
 * You may also contact one of the following for additional information:
 * Telenor ASA, Snaroyveien 30, N-1331 Fornebu, Norway (www.telenor.no)
 * Tellu AS, Hagalokkveien 13, N-1383 Asker, Norway (www.tellu.no)
 */
package org.coos.messaging.transport;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;

import java.nio.ByteBuffer;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.coos.messaging.Channel;
import org.coos.messaging.Message;
import org.coos.messaging.Processor;
import org.coos.messaging.ProcessorException;
import org.coos.messaging.Service;
import org.coos.messaging.Transport;
import org.coos.messaging.impl.DefaultMessage;
import org.coos.messaging.impl.DefaultProcessor;


/**
 * Represents one non-blocking tcp connection. Decodes messages by reading the
 * length from the first 4 bytes.
 *
 * @author Morten Versvik, Tellu AS
 *
 */
public class NioTCPTransport extends DefaultProcessor implements Transport, Service {

    /**
     * Initial buffer size.
     */
    private final static int BUFFER_SIZE = 8 * 1024;

    /**
     * 1 int is 4 bytes.
     */
    private static final int SIZE_POSITION = 4 - 1;
    protected List mailbox = Collections.synchronizedList(new LinkedList());
    protected Processor transportProcessor;
    private ByteBuffer outBuffer = null, inBuffer;
    SocketChannel sc;
    NioTCPTransportManager tm;
    private byte[] buffer = new byte[BUFFER_SIZE];
    private int pos = 0;
    private int newmsgLength = Integer.MAX_VALUE;
    protected int MAX_LENGTH = 8 * 1024; // Max length for a coos msg

    boolean partialRead = false;

    public NioTCPTransport(NioTCPTransportManager tm, Selector selector, SocketChannel sc) throws IOException {
        this.sc = sc;
        inBuffer = ByteBuffer.allocateDirect(sc.socket().getReceiveBufferSize());

        this.tm = tm;
    }

    @Override public void setChainedProcessor(Processor chainedProcessor) {
        this.transportProcessor = chainedProcessor;
    }

    public void receivedMessage(Message in) {

        try {
            transportProcessor.processMessage(in);
        } catch (ProcessorException e) {
            logger.error("ProcessorException ignored.", e);
        }
    }

    public int getMailboxSize() {
        return mailbox.size();
    }

    public Message getMessage() {
        return mailbox.remove(0);
    }

    @Override public void setChannel(Channel channel) {
    }

    @Override public void processMessage(Message msg) throws ProcessorException {
        String priStr = msg.getHeader(Message.PRIORITY);

        if (priStr != null) {
            int pri = Integer.valueOf(priStr);
            int idx = 0;

            for (Message message : mailbox) {
                String pr = message.getHeader(Message.PRIORITY);

                if (pr != null) {
                    int p = Integer.valueOf(pr);

                    if (pri < p) {
                        mailbox.add(idx, msg);

                        synchronized (this) {
                            this.notify();
                        }

                        break;
                    }
                }

                idx++;
            }
        } else {
            mailbox.add(msg);

            synchronized (this) {
                this.notify();
            }
        }

        // A new message was added, notify the writer.
        if (sc != null)
            tm.readyWrite(sc);
    }

    @Override public void start() throws Exception {
    }

    @Override public void stop() throws Exception {
    }

    public byte[] decode(ByteBuffer socketBuffer) throws IOException {

        // Reads until the buffer is empty or until a packet
        // is fully reassembled.
        while (socketBuffer.hasRemaining()) {

            // Copies into the temporary buffer
            byte data = socketBuffer.get();

            try {
                buffer[pos] = data;

                if (pos == SIZE_POSITION) { // Got size parameter

                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer);
                    DataInputStream din = new DataInputStream(bin);

                    newmsgLength = din.readInt() + SIZE_POSITION + 1;

                    if (newmsgLength > MAX_LENGTH)
                        throw new IOException("Packet too big");

                    if (buffer.length < newmsgLength)
                        buffer = new byte[newmsgLength];
                }
            } catch (IndexOutOfBoundsException e) {

                // We resize the buffer, shouldn't happen.
                throw new IOException("Packet too big. Maximum size allowed: " + BUFFER_SIZE + " bytes.", e);
            }

            pos++;

            // Check if it is the final byte of a packet.
            if (pos == newmsgLength) {
                newmsgLength = Integer.MAX_VALUE;

                // The current packet is fully reassembled. Return it
                byte[] newBuffer = new byte[pos];
                System.arraycopy(buffer, 0, newBuffer, 0, pos);
                pos = 0;

                return newBuffer;
            }
        }

        // No packet was reassembled. There is not enough data. Wait
        // for more data to arrive.
        return null;
    }

    /**
     * Reads and decodes bytes from socket
     * @return -1 disconnected, 0 = 0 read, 1 = >= 1 bytes read
     * @throws Exception
     */
    public int decodeFromSocket() throws Exception {

        if (!partialRead) {
            int readBytes = sc.read(inBuffer);

            if (readBytes == -1) {
                sc.close();
                tm.socketDisconnected(sc);

                return -1;
            }

            if ((readBytes == 0) && (inBuffer.position() == 0)) {
                return 0;
            }

            inBuffer.flip();
        }

        byte[] bb = decode(inBuffer);

        if (bb == null) {

            // Partial packet received. Must wait for more data. All the
            // contents
            // of inBuffer were processed by the protocol decoder. We can
            // delete it and prepare for more data.
            inBuffer.clear();
            partialRead = false;
        } else {
            // A packet was reassembled.

            Message msg;

            try {

                //              System.out.println(bb.length);
                msg = new DefaultMessage(new DataInputStream(new ByteArrayInputStream(bb)));

                //      System.out.println(msg);
                receivedMessage(msg);
                partialRead = true;
            } catch (Exception e) {
                logger.warn("Exception ignored", e);
            }

            // The inBuffer might still have some data left. Perhaps
            // the beginning of another packet. So don't clear it. Next
            // time reading is activated, we start by processing the inBuffer
            // again.
        }

        return 1;
    }

    public void handleWrite() throws Exception {

        if (outBuffer == null) {

            if (mailbox.size() == 0) {
                tm.doneWrite(sc);

                return;
            }

            Message msg = getMessage();
            outBuffer = ByteBuffer.wrap(msg.serialize());
        }

        sc.write(outBuffer);

        // Check if there are more to be written.
        if (!outBuffer.hasRemaining()) {

            // outBuffer was completely written, deactivate write intent.
            outBuffer = null;

            tm.doneWrite(sc);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy