edu.hawaii.soest.pacioos.text.SocketTextSource.java Source code

Java tutorial

Introduction

Here is the source code for edu.hawaii.soest.pacioos.text.SocketTextSource.java

Source

/**
 *  Copyright: 2013 Regents of the University of Hawaii and the
 *             School of Ocean and Earth Science and Technology
 *    Purpose: A class that provides properties and methods 
 *             for a simple instrument driver streaming data from a
 *             text-based, TCP socket connection.
 *
 *   Authors: Christopher Jones
 *
 * $HeadURL$
 * $LastChangedDate$
 * $LastChangedBy$
 * $LastChangedRevision$
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package edu.hawaii.soest.pacioos.text;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.rbnb.sapi.ChannelMap;
import com.rbnb.sapi.SAPIException;

/**
 * A class that provides properties and methods 
 * for a simple instrument driver streaming data from a
 * text-based, TCP socket connection.
 * @author cjones
 *
 */
public class SocketTextSource extends SimpleTextSource {

    private static final Log log = LogFactory.getLog(SocketTextSource.class);

    /* The FQDN or IP of the source instrument host */
    private String sourceHostName;

    /* The connection port of the source instrument host */
    private int sourceHostPort;

    /* The size of the ByteBuffer used to buffer the TCP stream from the instrument. */
    private int bufferSize = 8192;

    /* The state of the sample processing */
    protected int state = 0;

    /* The number of bytes in the sample as each byte is read from the stream */
    private int sampleByteCount = 0;

    /**
     * constructor: create an instance of the SerialTextSource 
     * @param xmlConfig 
     * @throws ConfigurationException 
     */
    public SocketTextSource(XMLConfiguration xmlConfig) throws ConfigurationException {
        super(xmlConfig);

    }

    /* (non-Javadoc)
     * @see edu.hawaii.soest.pacioos.text.SimpleTextSource#execute()
     */
    @Override
    protected boolean execute() {

        log.debug("SocketTextSource.execute() called.");
        // do not execute the stream if there is no connection
        if (!isConnected())
            return false;

        boolean failed = false;

        /* Get a connection to the instrument */
        SocketChannel socket = getSocketConnection();
        if (socket == null)
            return false;

        // while data are being sent, read them into the buffer
        try {
            // create four byte placeholders used to evaluate up to a four-byte 
            // window.  The FIFO layout looks like:
            //           -------------------------
            //   in ---> | One | Two |Three|Four |  ---> out
            //           -------------------------
            byte byteOne = 0x00, // set initial placeholder values
                    byteTwo = 0x00, byteThree = 0x00, byteFour = 0x00;

            // Create a buffer that will store the sample bytes as they are read
            ByteBuffer sampleBuffer = ByteBuffer.allocate(getBufferSize());

            // create a byte buffer to store bytes from the TCP stream
            ByteBuffer buffer = ByteBuffer.allocateDirect(getBufferSize());

            // while there are bytes to read from the socket ...
            while (socket.read(buffer) != -1 || buffer.position() > 0) {

                // prepare the buffer for reading
                buffer.flip();

                // while there are unread bytes in the ByteBuffer
                while (buffer.hasRemaining()) {
                    byteOne = buffer.get();

                    // log the byte stream
                    String character = new String(new byte[] { byteOne });
                    if (log.isDebugEnabled()) {
                        List<Byte> whitespaceBytes = new ArrayList<Byte>();
                        whitespaceBytes.add(new Byte((byte) 0x0A));
                        whitespaceBytes.add(new Byte((byte) 0x0D));
                        if (whitespaceBytes.contains(new Byte(byteOne))) {
                            character = new String(Hex.encodeHex((new byte[] { byteOne })));

                        }
                    }
                    log.debug("char: " + character + "\t" + "b1: "
                            + new String(Hex.encodeHex((new byte[] { byteOne }))) + "\t" + "b2: "
                            + new String(Hex.encodeHex((new byte[] { byteTwo }))) + "\t" + "b3: "
                            + new String(Hex.encodeHex((new byte[] { byteThree }))) + "\t" + "b4: "
                            + new String(Hex.encodeHex((new byte[] { byteFour }))) + "\t" + "sample pos: "
                            + sampleBuffer.position() + "\t" + "sample rem: " + sampleBuffer.remaining() + "\t"
                            + "sample cnt: " + sampleByteCount + "\t" + "buffer pos: " + buffer.position() + "\t"
                            + "buffer rem: " + buffer.remaining() + "\t" + "state: " + state);

                    // evaluate each byte to find the record delimiter(s), and when found, validate and
                    // send the sample to the DataTurbine.
                    int numberOfChannelsFlushed = 0;

                    if (getRecordDelimiters().length == 2) {
                        // have we hit the delimiters in the stream yet?
                        if (byteTwo == getFirstDelimiterByte() && byteOne == getSecondDelimiterByte()) {
                            sampleBuffer.put(byteOne);
                            sampleByteCount++;
                            // extract just the length of the sample bytes out of the
                            // sample buffer, and place it in the channel map as a 
                            // byte array.  Then, send it to the DataTurbine.
                            log.debug("Sample byte count: " + sampleByteCount);
                            byte[] sampleArray = new byte[sampleByteCount];
                            sampleBuffer.flip();
                            sampleBuffer.get(sampleArray);
                            String sampleString = new String(sampleArray, "US-ASCII");

                            if (validateSample(sampleString)) {
                                numberOfChannelsFlushed = sendSample(sampleString);

                            }

                            sampleBuffer.clear();
                            sampleByteCount = 0;
                            byteOne = 0x00;
                            byteTwo = 0x00;
                            byteThree = 0x00;
                            byteFour = 0x00;
                            log.debug("Cleared b1,b2,b3,b4. Cleared sampleBuffer. Cleared rbnbChannelMap.");

                        } else {
                            // still in the middle of the sample, keep adding bytes
                            sampleByteCount++; // add each byte found

                            if (sampleBuffer.remaining() > 0) {
                                sampleBuffer.put(byteOne);

                            } else {
                                sampleBuffer.compact();
                                log.debug("Compacting sampleBuffer ...");
                                sampleBuffer.put(byteOne);

                            }

                        }

                    } else if (getRecordDelimiters().length == 1) {
                        // have we hit the delimiter in the stream yet?
                        if (byteOne == getFirstDelimiterByte()) {
                            sampleBuffer.put(byteOne);
                            sampleByteCount++;
                            // extract just the length of the sample bytes out of the
                            // sample buffer, and place it in the channel map as a 
                            // byte array.  Then, send it to the DataTurbine.
                            byte[] sampleArray = new byte[sampleByteCount];
                            sampleBuffer.flip();
                            sampleBuffer.get(sampleArray);
                            String sampleString = new String(sampleArray, "US-ASCII");

                            if (validateSample(sampleString)) {
                                numberOfChannelsFlushed = sendSample(sampleString);

                            }

                            sampleBuffer.clear();
                            sampleByteCount = 0;
                            byteOne = 0x00;
                            byteTwo = 0x00;
                            byteThree = 0x00;
                            byteFour = 0x00;
                            log.debug("Cleared b1,b2,b3,b4. Cleared sampleBuffer. Cleared rbnbChannelMap.");

                        } else {
                            // still in the middle of the sample, keep adding bytes
                            sampleByteCount++; // add each byte found

                            if (sampleBuffer.remaining() > 0) {
                                sampleBuffer.put(byteOne);

                            } else {
                                sampleBuffer.compact();
                                log.debug("Compacting sampleBuffer ...");
                                sampleBuffer.put(byteOne);

                            }

                        }

                    } // end getRecordDelimiters().length

                    // shift the bytes in the FIFO window
                    byteFour = byteThree;
                    byteThree = byteTwo;
                    byteTwo = byteOne;

                } //end while (more unread bytes)

                // prepare the buffer to read in more bytes from the stream
                buffer.compact();

            } // end while (more socket bytes to read)
            socket.close();

        } catch (IOException e) {
            // handle exceptions
            // In the event of an i/o exception, log the exception, and allow execute()
            // to return false, which will prompt a retry.
            failed = true;
            log.error("There was a communication error in sending the data sample. The message was: "
                    + e.getMessage());
            if (log.isDebugEnabled()) {
                e.printStackTrace();
            }
            return !failed;

        } catch (SAPIException sapie) {
            // In the event of an RBNB communication  exception, log the exception, 
            // and allow execute() to return false, which will prompt a retry.
            failed = true;
            log.error("There was an RBNB error while sending the data sample. The message was: "
                    + sapie.getMessage());
            if (log.isDebugEnabled()) {
                sapie.printStackTrace();
            }
            return !failed;
        }

        return !failed;

    } // end if (  !isConnected() ) 

    /* (non-Javadoc)
     * @see org.nees.rbnb.RBNBBase#setArgs(org.apache.commons.cli.CommandLine)
     */
    @Override
    protected boolean setArgs(CommandLine cmd) {
        // TODO: Set fields as needed

        return false;
    }

    /**
     * Return the name (FQDN) or IP of the instrument host
     * 
     * @return sourceHostName  the name or IP of the instrument host 
     */
    public String getHostName() {
        return sourceHostName;

    }

    /**
     * Set the name (FQDN) or IP of the instrument host
     * 
     * @param sourceHostName  the name or IP of the instrument host
     */
    public void setHostName(String hostName) {
        this.sourceHostName = hostName;

    }

    /**
     * Return the connection port of the instrument host
     * 
     * @return sourceHostPort  the connection port of the instrument host 
     */
    public int getHostPort() {
        return sourceHostPort;

    }

    /**
     * Set the connection port of the instrument host
     * 
     * @param sourceHostPort  the connection port of the instrument host
     */
    public void setHostPort(int hostPort) {
        this.sourceHostPort = hostPort;

    }

    /**
    * A method used to the TCP socket of the remote source host for communication
    * @param host       the name or IP address of the host to connect to for the
    *                   socket connection (reading)
    * @param portNumber the number of the TCP port to connect to (i.e. 2604)
    */
    protected SocketChannel getSocketConnection() {

        String host = getHostName();
        int portNumber = new Integer(getHostPort()).intValue();
        SocketChannel dataSocket = null;

        try {

            // create the socket channel connection to the data source via the 
            // converter serial2IP converter      
            dataSocket = SocketChannel.open();
            dataSocket.connect(new InetSocketAddress(host, portNumber));

            // if the connection to the source fails, also disconnect from the RBNB
            // server and return null
            if (!dataSocket.isConnected()) {
                dataSocket.close();
                disconnect();
                dataSocket = null;
            }
        } catch (UnknownHostException ukhe) {

            log.info("Unable to look up host: " + host + "\n");
            disconnect();
            dataSocket = null;
        } catch (IOException nioe) {
            log.info("Couldn't get I/O connection to: " + host + ":" + portNumber);
            disconnect();
            dataSocket = null;
        }
        return dataSocket;

    }

    /**
     * A method that sets the size, in bytes, of the ByteBuffer used in streaming 
     * data from a source instrument via a TCP connection
     */
    public int getBufferSize() {
        return this.bufferSize;
    }

    /**
      * A method that sets the size, in bytes, of the ByteBuffer used in streaming 
      * data from a source instrument via a TCP connection
      *
      * @param bufferSize  the size, in bytes, of the ByteBuffer
      */
    public void setBuffersize(int bufferSize) {
        this.bufferSize = bufferSize;
    }

}