com.slytechs.jnetstream.livecapture.AbstractLiveCapture.java Source code

Java tutorial

Introduction

Here is the source code for com.slytechs.jnetstream.livecapture.AbstractLiveCapture.java

Source

/**
 * Copyright (C) 2007 Sly Technologies, Inc. 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 2.1 of the License, or (at your option) any later version. This
 * library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
package com.slytechs.jnetstream.livecapture;

import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jnetstream.capture.CaptureType;
import org.jnetstream.capture.LiveCapture;
import org.jnetstream.capture.LiveCaptureDevice;
import org.jnetstream.capture.LiveIterator;
import org.jnetstream.capture.LivePacket;
import org.jnetstream.filter.Filter;
import org.jnetstream.packet.ProtocolFilterTarget;

import com.slytechs.utils.collection.IOIterator.IteratorAdapter;
import com.slytechs.utils.io.IORuntimeException;

/**
 * @author Mark Bednarczyk
 * @author Sly Technologies, Inc.
 */
public abstract class AbstractLiveCapture implements LiveSource, LiveDispatchable, LiveCapture {

    protected static final Log logger = LogFactory.getLog(LiveCapture.class);

    private static final AtomicReference<IOException> ioError = new AtomicReference<IOException>();

    private static final String DISPATCHED = "DISPATCH";

    protected int count;

    protected int dispatched = 0;

    protected final LiveCaptureDevice[] devices;

    protected LiveClient client;

    protected Filter<ProtocolFilterTarget> filter;

    private boolean openState = true;

    private LiveIterator packetIterator;

    protected boolean promiscuous;

    protected int snaplen;

    protected int timeout;

    /**
     * Currently active worker thread. If null, no thread is running
     */
    @SuppressWarnings("unused")
    private volatile Thread[] workers;

    /**
     * @param captureDevice
     * @param captureCount
     * @param snaplen
     * @param promiscuous
     * @param timeout
     * @param filter
     */
    public AbstractLiveCapture(final LiveCaptureDevice[] captureDevice, int captureCount, int snaplen,
            boolean promiscuous, int timeout, Filter<ProtocolFilterTarget> filter) {
        this.devices = captureDevice;
        this.count = captureCount;
        this.snaplen = snaplen;
        this.promiscuous = promiscuous;
        this.timeout = timeout;
        this.filter = filter;
    }

    /**
     * Tells the open implementation dependent capture object to capture count
     * number of packets and then return.
     * 
     * @param count
     *          number of packets to capture and return or 0 for infinate
     * @param index
     *          TODO
     * @throws IOException
     *           any IO errors during the capture
     */
    protected abstract void capture(int count, int index) throws IOException;

    /**
     * Flags the capture closed and waits for all the workers to exit
     */
    public void close() throws IOException {
        this.openState = false;

        for (Thread worker : workers) {
            if (worker == null) {
                continue;
            }

            try {
                worker.join();
            } catch (InterruptedException e) {
                break;
            }
        }

        client.getPacketQueue().clear();
    }

    /**
     * Dispatches a packet that is ready to clients. A concurrent blocking queue
     * is used to pass packets between this server thread and clients. Each client
     * maintains its own queue through which the packet is exchanged. If
     * particular clients queue is full, the packet is dropped and the appropriate
     * counter incremented to indicate a drop, on this particular client's queue.
     */
    public void dispatch(final LivePacket packet) {
        if (client == null) {
            return; // Nothing to do, ignore any packets received
        }

        if (logger.isTraceEnabled()) {
            logger.trace("Received packet");
        }

        final BlockingQueue<LivePacket> queue = client.getPacketQueue();

        if (queue.remainingCapacity() == 0) {
            client.incrementDropCounter(1);

        } else {
            queue.offer(packet);
        }

        synchronized (DISPATCHED) {
            this.dispatched++;
        }
    }

    public Filter<ProtocolFilterTarget> getFilter() {
        return filter;
    }

    /**
     * Gets the current pending IO exception which needs to be reported
     * 
     * @return IO exception which needs to be reported.
     */
    public IOException getIOException() {
        return ioError.get();
    }

    public LiveCaptureDevice[] getOpenCaptureDevices() {
        return devices;
    }

    public LiveIterator getPacketIterator() throws IOException {
        return getPacketIterator(count);
    }

    public LiveIterator getPacketIterator(int count) throws IOException {

        if (packetIterator == null) {
            this.packetIterator = new LiveIteratorImpl(this);
            this.client = (LiveClient) packetIterator;

            /*
             * Start the capture in the background
             */
            start(count);
        }

        return packetIterator;
    }

    public int getSnaplen() {
        return snaplen;
    }

    public int getTimeout() {
        return timeout;
    }

    public CaptureType getType() {
        return CaptureType.LiveCapture;
    }

    /**
     * Checks if there is a pending IO exception to be reported
     * 
     * @return true means that there is a pending exception, otherwise false
     */
    public boolean hasIOException() {
        return ioError.get() != null;
    }

    public boolean isMutable() {
        return false;
    }

    public boolean isOpen() throws IOException {
        return openState;
    }

    public boolean isPromisuous() {
        return promiscuous;
    }

    public boolean isRunning() {
        synchronized (DISPATCHED) {
            return workers != null && (count == 0 || dispatched < count);
        }
    }

    public Iterator<LivePacket> iterator() {
        try {
            return new IteratorAdapter<LivePacket>(getPacketIterator());
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
    }

    public void setFilter(Filter<ProtocolFilterTarget> filter) throws IOException {
        this.filter = filter;
    }

    /**
     * Captures count packets using an open capture session. Once the capture
     * session has been closed it can not be opened again.
     * 
     * @param count
     * @throws IOException
     */
    private void start(final int count) throws IOException {
        if (isOpen() == false) {
            throw new IOException("Capture session is closed");
        }

        /**
         * Check if we still have running workers
         */
        if (workers != null) {
            return;
        }

        this.dispatched = 0;

        if (workers == null) {
            workers = new Thread[devices.length];

            /*
             * Initialize with dummy data, so that the array is not full of nulls,
             * which would break the below loop if early workers start exitting
             * quickly and reseting workers back to null as well.
             */
            Arrays.fill(workers, Thread.currentThread());
        }

        for (int i = 0; i < devices.length; i++) {
            final int index = i;

            final String name = "Live-" + devices[i].getDisplayName();

            /*
             * Our capture session worker thread. Closes the LiveCapture when its done
             */
            workers[i] = new Thread(new Runnable() {

                public void run() {

                    try {
                        capture(count, index);
                    } catch (IOException e) {
                        /*
                         * The IO exception will be reported at next available opportunity
                         * such as in LiveIterator's next or hasNext method calls.
                         */
                        ioError.set(e);

                    } finally {
                        workers[index] = null;

                        /*
                         * Now check if there are any workers left, if not then reset the
                         * workers variable, as a flag
                         */
                        int j = 0;
                        for (; j < workers.length; j++) {
                            if (workers[j] != null) {
                                break;
                            }
                        }
                        if (j == workers.length) {
                            workers = null;
                        }

                        Thread.yield();
                    }
                }

            }, name);

            workers[i].start();
        }
    }

}