/*_##########################################################################
_##
_## Copyright (C) 2011 Kaito Yamada
_##
_##########################################################################
*/
package com.github.kaitoy.sneo.network;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.pcap4j.packet.Packet;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import com.github.kaitoy.sneo.util.NamedThreadFactory;
public abstract class PacketReceiver {
protected static final LogAdapter logger
= LogFactory.getLogger(PacketReceiver.class);
private static final long AWAIT_TERMINATION_TIMEOUT = 2000;
private static final TimeUnit AWAIT_TERMINATION_TIMEOUT_UNIT
= TimeUnit.MILLISECONDS;
private final String name;
private final StoppableLinkedBlockingQueue<PacketContainer> recvPacketQueue
= new StoppableLinkedBlockingQueue<PacketContainer>(
NetworkPropertiesLoader.getPacketQueueSize()
);
private final ExecutorService packetTakerExecutor;
private final ExecutorService packetProcessorThreadPool;
private final Object thisLock = new Object();
private Future<?> packetTakerFuture;
private volatile boolean running = false;
public PacketReceiver(String name) {
this.name = name;
this.packetTakerExecutor
= Executors.newSingleThreadExecutor(
new NamedThreadFactory(
name + "_" + PacketTaker.class.getSimpleName(),
true
)
);
this.packetProcessorThreadPool
= Executors.newCachedThreadPool(
new NamedThreadFactory(name + "_packetProcessor", true)
);
}
public String getName() {
return name;
}
public BlockingQueue<PacketContainer> getRecvPacketQueue() {
return recvPacketQueue;
}
public void start() {
synchronized (thisLock) {
if (isRunning()) {
logger.warn("Already started");
return;
}
recvPacketQueue.start();
packetTakerFuture = packetTakerExecutor.submit(new PacketTaker());
running = true;
}
}
public void stop() {
synchronized (thisLock) {
if (!isRunning()) {
logger.warn("Already stopped");
return;
}
packetTakerFuture.cancel(true);
recvPacketQueue.stop();
running = false;
}
}
public void shutdown() {
synchronized (thisLock) {
if (running) {
stop();
}
packetTakerExecutor.shutdown();
packetProcessorThreadPool.shutdown();
try {
boolean terminated
= packetTakerExecutor.awaitTermination(
AWAIT_TERMINATION_TIMEOUT,
AWAIT_TERMINATION_TIMEOUT_UNIT
);
if (!terminated) {
logger.warn("Couldn't terminate packetTakerExecutor.");
}
terminated
= packetProcessorThreadPool.awaitTermination(
AWAIT_TERMINATION_TIMEOUT,
AWAIT_TERMINATION_TIMEOUT_UNIT
);
if (!terminated) {
logger.warn("Couldn't terminate packetProcessorThreadPool.");
}
} catch (InterruptedException e) {
logger.warn(e);
}
}
logger.info("A packet receiver has been shutdown.");
}
public boolean isRunning() {
return running;
}
protected abstract void process(PacketContainer pc);
private class PacketTaker implements Runnable {
public void run() {
logger.info("start.");
while (isRunning()) {
try {
final PacketContainer packet = recvPacketQueue.take();
packetProcessorThreadPool.execute(
new Runnable() {
public void run() {
process(packet);
}
}
);
} catch (InterruptedException e) {
break;
}
}
logger.info("stopped.");
}
}
public static class PacketContainer {
private final Packet packet;
private final NetworkInterface src;
public PacketContainer(Packet packet, NetworkInterface src) {
this.packet = packet;
this.src = src;
}
public Packet getPacket() {
return packet;
}
public NetworkInterface getSrc() {
return src;
}
}
}