package org.cad.interruptus.service; import com.google.gson.Gson; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.zip.DataFormatException; import java.util.zip.Inflater; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cad.interruptus.Message; public class MessageService { final Log logger = LogFactory.getLog(getClass()); final Gson gson; public MessageService(final Gson gson) { this.gson = gson; } public List<Message> extractMessages(final byte[] input) throws Exception { // Max UDP packet size without fragmentation = 1500 bytes (MTU on ethernet without jumbo frames) - IP header (60 bytes max) - UDP header (8 bytes) = 1432 bytes // Ideally if we're trying to squeeze every little bit of performance, we should require clients to do path MTU discovery and encode // pre-compression length in first 4 bytes of payload. We would also enfore that that UDP packet size after compression be < 1432 // This would allow us to size buffers appropriately as well as have the lowest possible amount of TCP/IP header overhead. // See http://tools.ietf.org/html/rfc5405#section-3.2 // http://stackoverflow.com/questions/973439/how-to-set-the-dont-fragment-df-flag-on-a-socket // Should do some testing on alternate compression algorithms to see what gives us best bang for the buck. Gut feeling is that zlib reduces by > 50% // as json encoded metrics have lots of repetition. final List<Message> list = new ArrayList<>(); final String json = getJsonString(input); final StringTokenizer tokenizer = new StringTokenizer(json, "\n"); while (tokenizer.hasMoreTokens()) { final String jsonLine = tokenizer.nextToken(); final Message message = gson.fromJson(jsonLine, Message.class); list.add(message); } return list; } private String getJsonString(byte[] input) throws DataFormatException, UnsupportedEncodingException { if ((char) input[0] == '{') { // Very cheezy ... return new String(input); } // Max UDP packet size, worst case scenario until we get client to support encoding payload uncmopressed length. final byte[] restored = new byte[65507]; final Inflater decompresser = new Inflater(); decompresser.setInput(input,0,input.length); int resultLength = decompresser.inflate(restored); decompresser.end(); return new String(restored, 0, resultLength, "UTF-8"); } }