package pctelelog; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.socket.DatagramPacket; import io.netty.util.ReferenceCountUtil; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Hashtable; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import pctelelog.events.AbstractEvent; /** * A helper class that tracks Packets received and * rebuilds an event once all are available. * * @author Jeremy May * */ public class PacketHandler extends ChannelInboundHandlerAdapter { private final Logger logger = LogManager.getLogger(PacketHandler.class); private final int PRUNE_TIME = 5 * (60 * 1000); // 5 minutes private Hashtable<Long, PacketSet> packets = new Hashtable<Long, PacketSet>(); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = getSenderData(msg); byte[] data = new byte[buf.readableBytes()]; buf.readBytes(data); // Start building the event Packet packet = Packet.deserialize(data); logger.debug("PACKET: " + packet.getId() + ":" + (packet.getSequence()+1) + "/" + packet.getMaxSequency()); AbstractEvent event = rebuild(packet); if(event != null) { InetAddress remote = getSenderAddress(ctx, msg); event = EventDeviceResolver.resolveDevice(remote, event); ctx.fireChannelRead(event); logger.debug("Event Fired: " + event.toString()); } ReferenceCountUtil.release(msg); } private InetAddress getSenderAddress(ChannelHandlerContext ctx, Object msg) { InetAddress addr = null; if(msg instanceof DatagramPacket) { DatagramPacket dgram = (DatagramPacket)msg; addr = dgram.sender().getAddress(); } else if(msg instanceof ByteBuf) { InetSocketAddress tcp = (InetSocketAddress)ctx.channel().remoteAddress(); addr = tcp.getAddress(); } return addr; } private ByteBuf getSenderData(Object msg) { if(msg instanceof DatagramPacket) { // UDP DatagramPacket dgram = (DatagramPacket)msg; return dgram.content(); } else if(msg instanceof ByteBuf) { // TCP return (ByteBuf)msg; } else { // No idea return null; } } public void prune() { long time = System.currentTimeMillis(); Set<Long> ids = packets.keySet(); for(Long id : ids) { long diff = time - id.longValue(); if(diff > PRUNE_TIME) { remove(id); logger.debug("Pruned: " + id); } } } /** * Attempt to rebuild the fragmented JSON Event * * @param packet A packet to rebuild * @return An AbstractEvent or null if more packets are still needed */ private AbstractEvent rebuild(Packet packet) { Long id = Long.valueOf(packet.getId()); prune(); // Check if packet id is already in packets if(packet.getMaxSequency() == 1) { String json = new String(packet.getData()); try { AbstractEvent event = EventSerializer.deserialize(json); if(event != null) return event; } catch (IOException e) { e.printStackTrace(); } } else if(packets.containsKey(id)) { put(id, packet); // Check if we have all packets if(packets.get(id).size() == packet.getMaxSequency()) { logger.debug("Rebuild Event"); // Rebuild and return Event ByteBuffer buffer = ByteBuffer.allocate((int)Packet.DATA_SPLIT * packet.getMaxSequency()); PacketSet set = packets.get(id); for(int i=0; i < set.size(); i++) { Packet p = set.get(i); logger.debug("Packet " + p.getSequence() + "=" + p.getData()); buffer.put(p.getData()); } try { String json = new String(buffer.array()); AbstractEvent event = EventSerializer.deserialize(json); packets.remove(id); return event; } catch(IOException e) { e.printStackTrace(); } } } else { logger.debug("Add to set"); put(id, packet); } return null; } private void put(Long id, Packet packet) { PacketSet pack; if(packets.containsKey(id)) { pack = packets.get(id); } else { logger.debug("Creating new packet set for id: " + id); pack = new PacketSet(packet.getMaxSequency()); } pack.add(packet.getSequence(), packet); logger.debug("ADD Packet Set("+ id+ "): " + (packet.getSequence()+1) + "/" + packet.getMaxSequency() ); packets.put(id, pack); } private void remove(Long id) { packets.remove(id); } }