diff --git a/org.intrace/.classpath b/org.intrace/.classpath index 4e3a570..f873f21 100644 --- a/org.intrace/.classpath +++ b/org.intrace/.classpath @@ -1,16 +1,15 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/org.intrace/build.xml b/org.intrace/build.xml index 73bb5a3..3dff713 100644 --- a/org.intrace/build.xml +++ b/org.intrace/build.xml @@ -23,7 +23,7 @@ classpath="./lib/jarbundler-2.2.0.jar"/> - + @@ -44,7 +44,7 @@ - @@ -94,7 +94,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -186,7 +186,7 @@ - @@ -220,7 +220,7 @@ - + @@ -233,7 +233,7 @@ - + diff --git a/org.intrace/lib/easymock-2.5.2.jar b/org.intrace/lib/easymock-2.5.2.jar deleted file mode 100644 index 97c8bf0..0000000 Binary files a/org.intrace/lib/easymock-2.5.2.jar and /dev/null differ diff --git a/org.intrace/lib/easymock-3.4.jar b/org.intrace/lib/easymock-3.4.jar new file mode 100644 index 0000000..e1118b2 Binary files /dev/null and b/org.intrace/lib/easymock-3.4.jar differ diff --git a/org.intrace/lib/easymockclassextension-2.5.2.jar b/org.intrace/lib/easymockclassextension-2.5.2.jar deleted file mode 100644 index 4f518c8..0000000 Binary files a/org.intrace/lib/easymockclassextension-2.5.2.jar and /dev/null differ diff --git a/org.intrace/lib/intrace-agent.jar b/org.intrace/lib/intrace-agent.jar index abd8a3c..f616136 100644 Binary files a/org.intrace/lib/intrace-agent.jar and b/org.intrace/lib/intrace-agent.jar differ diff --git a/org.intrace/src/org/intrace/agent/AgentSettings.java b/org.intrace/src/org/intrace/agent/AgentSettings.java index 7367a99..a9fb162 100644 --- a/org.intrace/src/org/intrace/agent/AgentSettings.java +++ b/org.intrace/src/org/intrace/agent/AgentSettings.java @@ -23,7 +23,6 @@ public class AgentSettings private int actualServerPort = -1; // Dynamic settings - private boolean gzipEnabled = false; private String[] classRegex = new String[0]; private String[] excludeClassRegex = new String[0]; private boolean instruEnabled = true; @@ -39,7 +38,6 @@ public class AgentSettings { // Copy all static state and dynamic settings actualServerPort = oldInstance.getActualServerPort(); - gzipEnabled = oldInstance.isGzipEnabled(); classRegex = oldInstance.getClassRegex(); excludeClassRegex = oldInstance.getExcludeClassRegex(); instruEnabled = oldInstance.isInstrumentationEnabled(); @@ -47,12 +45,6 @@ public class AgentSettings verboseMode = oldInstance.isVerboseMode(); } - public boolean isGzipEnabled() { - return gzipEnabled; - } - public void setGzipEnabled(boolean val) { - gzipEnabled = val; - } public void parseArgs(String args) { @@ -116,14 +108,6 @@ public void parseArgs(String args) { waitStart = false; } - else if (arg.toLowerCase(Locale.ROOT).equals(AgentConfigConstants.GZIP + "true")) - { - gzipEnabled = true; - } - else if (arg.toLowerCase(Locale.ROOT).equals(AgentConfigConstants.GZIP + "false")) - { - gzipEnabled = false; - } else if (arg.startsWith(AgentConfigConstants.CLASS_REGEX)) { String classRegexStr = arg.replace(AgentConfigConstants.CLASS_REGEX, ""); @@ -194,7 +178,6 @@ public void parseArgs(String args) { // Output key settings String currentSettings = ""; - currentSettings += "GZip Enabled : " + gzipEnabled + "\n"; currentSettings += "Class Regex : " + Arrays.toString(classRegex) + "\n"; currentSettings += "Exclude Class Regex : " + Arrays.toString(excludeClassRegex) + "\n"; currentSettings += "Tracing Enabled : " + instruEnabled + "\n"; @@ -208,8 +191,6 @@ public void parseArgs(String args) Map settingsMap = new HashMap(); settingsMap.put(AgentConfigConstants.INSTRU_ENABLED, Boolean.toString(instruEnabled)); - settingsMap.put(AgentConfigConstants.GZIP, - Boolean.toString(gzipEnabled)); settingsMap.put(AgentConfigConstants.CLASS_REGEX, getPatternString(classRegex)); settingsMap.put(AgentConfigConstants.EXCLUDE_CLASS_REGEX, diff --git a/org.intrace/src/org/intrace/agent/ClassTransformer.java b/org.intrace/src/org/intrace/agent/ClassTransformer.java index 85f774a..114a116 100644 --- a/org.intrace/src/org/intrace/agent/ClassTransformer.java +++ b/org.intrace/src/org/intrace/agent/ClassTransformer.java @@ -308,8 +308,9 @@ public class ClassTransformer implements ClassFileTransformer originalClassfile); if (shouldInstrument && !"java.lang.Thread".equals(className)) { -// System.out.println("!! Instrumenting class: " + compclass); - + if (settings.isVerboseMode()) + TraceHandler.INSTANCE.writeTraceOutput("DEBUG: !! Instrumenting class: " + compclass); + if (settings.saveTracedClassfiles()) { writeClassBytes(originalClassfile, internalClassName + "_src.class"); @@ -528,9 +529,6 @@ public class ClassTransformer implements ClassFileTransformer Integer.toString(modifiedClasses.size())); return settingsMap; } - public boolean isGzipEnabled() { - return settings.isGzipEnabled(); - } /** * Handle a message and return a response. @@ -556,12 +554,6 @@ public class ClassTransformer implements ClassFileTransformer // System.out.println("## Settings Changed"); setInstrumentationEnabled(settings.isInstrumentationEnabled()); } - else if (oldSettings.isGzipEnabled() != settings.isGzipEnabled()) - { - //For coding simplicity and user clarity, the gzip parameter can only be set on agent side - //So, return to the old setting, which originated with the intrace agent command line parameter -- [gzip-true - settings.setGzipEnabled(oldSettings.isGzipEnabled()); - } else if (!Arrays.equals(oldSettings.getClassRegex(), settings.getClassRegex())) { // System.out.println("## Settings Changed"); @@ -693,20 +685,24 @@ public class ClassTransformer implements ClassFileTransformer broadcastProgress(countNumClasses, totalNumClasses); for (ComparableClass klass : klasses) { + if (settings.isVerboseMode()) + TraceHandler.INSTANCE.writeTraceOutput("DEBUG: !! ClassTransformer#instrumentKlasses [" + klass.klass.getName() + "]"); try { inst.retransformClasses(klass.klass); countNumClasses++; - if ((countNumClasses % 10) == 0) + if ((countNumClasses % 100) == 0) { broadcastProgress(countNumClasses, totalNumClasses); } } catch (Throwable e) { - // Write exception to stdout -// System.out.println(klass.klass.getName()); + String error = "Error instrumenting [" + klass.klass.getName() + "]"; + if (settings.isVerboseMode()) + TraceHandler.INSTANCE.writeTraceOutput("DEBUG: !! " + error); + System.err.println(error); e.printStackTrace(); } } diff --git a/org.intrace/src/org/intrace/client/gui/helper/InTraceUI.java b/org.intrace/src/org/intrace/client/gui/helper/InTraceUI.java index 08f7de9..866270d 100644 --- a/org.intrace/src/org/intrace/client/gui/helper/InTraceUI.java +++ b/org.intrace/src/org/intrace/client/gui/helper/InTraceUI.java @@ -2042,11 +2042,6 @@ public class InTraceUI implements ISocketCallback, IControlConnectionListener { return settingsData.netOutEnabled; } - @Override - public boolean isGzipEnabled() - { - return settingsData.gzipEnabled; - } }; networkTraceThread = new NetworkDataReceiverThread(remoteAddress, networkTracePort, config, outputTabs.textOutputTab.filterThread); diff --git a/org.intrace/src/org/intrace/client/gui/helper/NetworkDataReceiverThread.java b/org.intrace/src/org/intrace/client/gui/helper/NetworkDataReceiverThread.java index 25970aa..07b421b 100644 --- a/org.intrace/src/org/intrace/client/gui/helper/NetworkDataReceiverThread.java +++ b/org.intrace/src/org/intrace/client/gui/helper/NetworkDataReceiverThread.java @@ -6,7 +6,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; -import org.intrace.shared.Base64; +import org.intrace.shared.SerializationHelper; public class NetworkDataReceiverThread implements Runnable @@ -14,7 +14,6 @@ public class NetworkDataReceiverThread implements Runnable public static interface INetworkOutputConfig { public boolean isNetOutputEnabled(); - public boolean isGzipEnabled(); } private final Socket traceSocket; @@ -57,13 +56,13 @@ public class NetworkDataReceiverThread implements Runnable { if (outputConfig.isNetOutputEnabled()) { - if (outputConfig.isGzipEnabled()) { - byte[] tmp = Base64.decode(traceLine); - traceLine = new String(tmp); - } traceThread.addTraceLine(traceLine); } } + } else if ( data instanceof byte[]) { + String[] myObj1 = SerializationHelper.fromWire( (byte[])data); + for(String s : myObj1) + traceThread.addTraceLine(s); } } } diff --git a/org.intrace/src/org/intrace/client/gui/helper/ParsedSettingsData.java b/org.intrace/src/org/intrace/client/gui/helper/ParsedSettingsData.java index 2b04b6c..d9b8f08 100644 --- a/org.intrace/src/org/intrace/client/gui/helper/ParsedSettingsData.java +++ b/org.intrace/src/org/intrace/client/gui/helper/ParsedSettingsData.java @@ -16,7 +16,6 @@ public class ParsedSettingsData public boolean entryExitEnabled; public boolean branchEnabled; - public boolean gzipEnabled = false; public boolean truncArraysEnabled; public boolean argsEnabled; public boolean exitStackTrace = false; @@ -37,15 +36,6 @@ public class ParsedSettingsData public ParsedSettingsData(Map settingsMap) { this.settingsMap = settingsMap; - - if ("true".equals(settingsMap.get(AgentConfigConstants.GZIP))) - { - gzipEnabled = true; - } - else - { - gzipEnabled = false; - } if ("true".equals(settingsMap.get(AgentConfigConstants.EXIT_STACK_TRACE))) { diff --git a/org.intrace/src/org/intrace/output/BatchScheduler.java b/org.intrace/src/org/intrace/output/BatchScheduler.java new file mode 100644 index 0000000..91e986d --- /dev/null +++ b/org.intrace/src/org/intrace/output/BatchScheduler.java @@ -0,0 +1,42 @@ +package org.intrace.output; + +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; + +/** + * Given an arrival time (millis), calculate departure time (millis). + * An instance of this object that arrives in a particular time window will depart in the + * -- current window, if BatchWindowMultiplier = 1 + * -- subsequent window , if BatchWindowMultiplier = 2 + * where window size is defined by the given IBatchSchedulerConfig. + * + * Designed to work with java.util.concurrent.DelayQueue + * https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/DelayQueue.html + * @author erikostermueller + * + */ +public class BatchScheduler { + + IBatchSchedulerConfig config = null; + private long arrivalTimeMillis = 0L; + public BatchScheduler(long val, IBatchSchedulerConfig config) { + this.arrivalTimeMillis = val; + this.config = config; + } + + + public long getArrivalTimeMillis() { + return this.arrivalTimeMillis; + } + + public long getDepartureTimeMillis() { + + long aLittleTooFarIntoFuture = + this.getArrivalTimeMillis() + + (this.config.getDrainInterval() * this.config.getDrainIntervalMultiplier() ); + + long offset = aLittleTooFarIntoFuture % this.config.getDrainInterval(); + return aLittleTooFarIntoFuture - offset; + } + +} diff --git a/org.intrace/src/org/intrace/output/IBatchSchedulerConfig.java b/org.intrace/src/org/intrace/output/IBatchSchedulerConfig.java new file mode 100644 index 0000000..d8b84d1 --- /dev/null +++ b/org.intrace/src/org/intrace/output/IBatchSchedulerConfig.java @@ -0,0 +1,7 @@ +package org.intrace.output; + +public interface IBatchSchedulerConfig { + int getDrainInterval(); + int getDrainIntervalMultiplier(); + +} diff --git a/org.intrace/src/org/intrace/output/NetworkDataSenderThread.java b/org.intrace/src/org/intrace/output/NetworkDataSenderThread.java index a1e5416..651e35d 100644 --- a/org.intrace/src/org/intrace/output/NetworkDataSenderThread.java +++ b/org.intrace/src/org/intrace/output/NetworkDataSenderThread.java @@ -4,139 +4,343 @@ import java.io.IOException; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Delayed; +//import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.TimeUnit; import org.intrace.agent.server.AgentClientConnection; -import org.intrace.shared.Base64; +import org.intrace.shared.SerializationHelper; -public class NetworkDataSenderThread extends InstruRunnable -{ - private boolean alive = true; - private final ServerSocket networkSocket; - private Socket traceSendingSocket = null; - private final BlockingQueue outgoingData = new LinkedBlockingQueue(30); - private Map set = new HashMap(); - private final AgentClientConnection connection; +public class NetworkDataSenderThread extends InstruRunnable { + private long traceEventSequence = 0; + private static final int EST_EVENT_COUNT_PER_BATCH = 16 * 1024; + private static final int BURST_SIZE = 16 * 1024; + private static final int DRAIN_INTERVAL_MS = 1000; + /** + * All BatchQueueEle instances will point to this singleton, to avoid + * unnecessary GC overhead. I made this public so that in the future, the + * parameters could be changed on the fly. + */ + public static IBatchSchedulerConfig batchSchedulerConfig = new IBatchSchedulerConfig() { + @Override + public int getDrainInterval() { + return DRAIN_INTERVAL_MS; + } - public NetworkDataSenderThread(AgentClientConnection connection, ServerSocket networkSocket) - { - this.connection = connection; - this.networkSocket = networkSocket; - } + @Override + public int getDrainIntervalMultiplier() { + return 1; + } + }; - public void start(Map set) - { - this.set = set; + /** + * An internal wrapper class for an individual trace event. Enables use of + * java.util.concurrent.DelayQueue to schedule multiples of these to be + * shipped to the client in a single batch, to avoid previous chatty and + * poorly performing behavior of a single socket write per trace event. + * + * Because of the excess GC required, I'm not thrilled about needing this + * wrapper instance for every trace event. However, the DelayQueue solution + * is otherwise pretty elegant. + * + * @author erikostermueller + * + */ + public static class TraceEventForBatch implements Delayed { + private Object traceEventText = null; + private long arrivalTimeMillis = 0L; + private long traceEventSequence = 0L; + private BatchScheduler batchScheduler = null; - Thread networkThread = new Thread(this); - networkThread.setDaemon(true); - networkThread.setName(Thread.currentThread().getName() - + " - Network Data Sender"); - networkThread.start(); - } + public TraceEventForBatch(Object val, long arrivalTimeMillis, + long traceEventSequence) { + this.traceEventText = val; + this.batchScheduler = new BatchScheduler(arrivalTimeMillis, + batchSchedulerConfig); + this.traceEventSequence = traceEventSequence; + } - private void stop() - { - try - { - if (connection != null) - { - connection.setTraceConnEstablished(false); - } - alive = false; - networkSocket.close(); - if (traceSendingSocket != null) - { - traceSendingSocket.close(); - } - } - catch (IOException e) - { - // Throw away - } - set.remove(this); -// System.out.println("## Trace Connection Disconnected"); - } + public Object getTraceEvent() { + return this.traceEventText; + } - public void queueData(Object data) - { - try - { - while (alive && !outgoingData.offer(data, 100, TimeUnit.MILLISECONDS)) - { - // Do nothing - work is done above - } - } - catch (InterruptedException e) - { - // Throw away - } - } - - public void runMethod() - { - try - { - traceSendingSocket = networkSocket.accept(); -// System.out.println("## Trace Connection Established"); - traceSendingSocket.setKeepAlive(true); - - if (connection != null) - { - connection.setTraceConnEstablished(true); - } - - ObjectOutputStream traceWriter = new ObjectOutputStream( - traceSendingSocket - .getOutputStream()); - // Ready to handle data - set.put(this, new Object()); - - while (true) - { - Object traceLine = outgoingData.poll(5, TimeUnit.SECONDS); - if (traceLine != null) - { - if (traceLine instanceof String && connection.getTransformer().isGzipEnabled()) { - String tmp = (String)traceLine; - traceLine = Base64.encodeBytes(tmp.getBytes(), Base64.GZIP); - } - traceWriter.writeObject(traceLine); - } - else - { - traceWriter.writeObject("NOOP"); - } - traceWriter.flush(); - traceWriter.reset(); - } - } - catch (InterruptedException ex) - { - stop(); - } - catch (IOException ex) - { - stop(); - } - } + @Override + public int compareTo(Delayed o) { + long rc = this.getDelay(TimeUnit.MILLISECONDS) + - o.getDelay(TimeUnit.MILLISECONDS); + return (int) rc; + } - public void gracefulShutdown() - { - while(!outgoingData.isEmpty()) - { - try - { - Thread.sleep(100); - } - catch (InterruptedException e) - { - // Ignore - } - } - } + public long getTraceEventSequence() { + return this.traceEventSequence; + } + + /** + * "Expiration occurs when an element's getDelay(TimeUnit.NANOSECONDS) method returns a value less than or equal to zero." + * ....taken from here: + * https://docs.oracle.com/javase/7/docs/api/java/util + * /concurrent/DelayQueue.html + */ + @Override + public long getDelay(TimeUnit unit) { + long delay = this.batchScheduler.getDepartureTimeMillis() + - System.currentTimeMillis(); + return unit.convert(delay, TimeUnit.MILLISECONDS); + } + + } + + private boolean alive = true; + private final ServerSocket networkSocket; + private Socket traceSendingSocket = null; + // private final BlockingQueue outgoingData = new + // LinkedTransferQueue(); + private final BlockingQueue outgoingData = new DelayQueue(); + private Map set = new HashMap(); + private final AgentClientConnection connection; + + public NetworkDataSenderThread(AgentClientConnection connection, + ServerSocket networkSocket) { + this.connection = connection; + this.networkSocket = networkSocket; + } + + public void start(Map set) { + this.set = set; + + Thread networkThread = new Thread(this); + networkThread.setDaemon(true); + networkThread.setName(Thread.currentThread().getName() + + " - Network Data Sender"); + networkThread.start(); + } + + private void stop() { + try { + if (connection != null) { + connection.setTraceConnEstablished(false); + } + alive = false; + networkSocket.close(); + if (traceSendingSocket != null) { + traceSendingSocket.close(); + } + } catch (IOException e) { + // Throw away + } + set.remove(this); + // System.out.println("## Trace Connection Disconnected"); + } + + public void queueData(Object data) { + TraceEventForBatch dataWrapper = new TraceEventForBatch(data, + System.currentTimeMillis(), traceEventSequence++); + if (alive) + outgoingData.offer(dataWrapper); + } + + public void runMethod_OLD() { + final int HEARTBEAT_TIME_SECONDS = 5; + try { + traceSendingSocket = networkSocket.accept(); + // System.out.println("## Trace Connection Established"); + traceSendingSocket.setKeepAlive(true); + + if (connection != null) { + connection.setTraceConnEstablished(true); + } + + ObjectOutputStream traceWriter = new ObjectOutputStream( + traceSendingSocket.getOutputStream()); + // Ready to handle data + set.put(this, new Object()); + + List traceEventsForSingleBatch = new ArrayList( + EST_EVENT_COUNT_PER_BATCH); + List tmp = new ArrayList( + EST_EVENT_COUNT_PER_BATCH); + while (true) { + TraceEventForBatch traceLineWrapper = outgoingData.poll( + HEARTBEAT_TIME_SECONDS, TimeUnit.SECONDS); + if (traceLineWrapper != null) { + if (traceLineWrapper.getTraceEvent() instanceof String) { + traceEventsForSingleBatch.clear(); + tmp.clear(); + traceEventsForSingleBatch.add((String) traceLineWrapper + .getTraceEvent()); + outgoingData.drainTo(tmp, BURST_SIZE); + Collections.sort(tmp, + new Comparator() { // It all + // ends + // badly + // without + // this + // sort. + @Override + public int compare(TraceEventForBatch o1, + TraceEventForBatch o2) { + return (int) (o1 + .getTraceEventSequence() - o2 + .getTraceEventSequence()); + } + }); + /** + * This copy from-TraceEventForBatch-to-string-array is + * required to avoid 2 things: -- wire format where each + * trace event has the extra overhead/space of one long + * timestamp (the one used for DelayQueue.getDelay() ). + * -- Requiring TraceEventForBatch .class to be in + * client JVM. This will avoid versioning conflicts. + */ + for (TraceEventForBatch tefb : tmp) + traceEventsForSingleBatch.add((String) tefb + .getTraceEvent()); + + String[] eventsForOneBatch = new String[traceEventsForSingleBatch + .size()]; + traceEventsForSingleBatch.toArray(eventsForOneBatch); + byte[] wireData = SerializationHelper + .toWire(eventsForOneBatch); + traceWriter.writeObject(wireData); + } + } else { + traceWriter.writeObject("NOOP"); // If no events in the last + // HEARTBEAT_TIME_SECONDS, + // then send a NOOP to + // keep the tcp + // connection alive. + } + traceWriter.flush(); + traceWriter.reset(); + } + } catch (InterruptedException ex) { + stop(); + } catch (IOException ex) { + stop(); + } + } + + public void runMethod() { + final int HEARTBEAT_TIME_SECONDS = 5; + try { + traceSendingSocket = networkSocket.accept(); + // System.out.println("## Trace Connection Established"); + traceSendingSocket.setKeepAlive(true); + + if (connection != null) { + connection.setTraceConnEstablished(true); + } + + ObjectOutputStream traceWriter = new ObjectOutputStream( + traceSendingSocket.getOutputStream()); + // Ready to handle data + set.put(this, new Object()); + + while (true) { + TraceEventForBatch traceLineWrapper = outgoingData.poll( + HEARTBEAT_TIME_SECONDS, TimeUnit.SECONDS); + if (traceLineWrapper != null) { + if (traceLineWrapper.getTraceEvent() instanceof String) { + List typedAndSortedList = new ArrayList( + EST_EVENT_COUNT_PER_BATCH); + typedAndSortedList.add(traceLineWrapper); + while (outgoingData.drainTo(typedAndSortedList, BURST_SIZE) > 0) + ; + // It all ends badly without this sort + Collections.sort(typedAndSortedList, + new Comparator() { + @Override + public int compare(TraceEventForBatch o1, + TraceEventForBatch o2) { + return (int) (o1 + .getTraceEventSequence() - o2 + .getTraceEventSequence()); + } + }); + transmitBatch(traceWriter, typedAndSortedList, BURST_SIZE); + } else { + throw new RuntimeException( + "Found unsupported trace event of type [" + + traceLineWrapper.getTraceEvent() + .getClass().getName() + "]"); + } + } else { + traceWriter.writeObject("NOOP"); // If no events in the last + // HEARTBEAT_TIME_SECONDS, + // then send a NOOP to + // keep the tcp + // connection alive. + } + traceWriter.flush(); + traceWriter.reset(); + } + } catch (InterruptedException ex) { + stop(); + } catch (IOException ex) { + stop(); + } + } + + /** + * Using the give ObjectOutputStream, transmit the given list of events + * in one or more successive bursts of no more than burstSize events. + * + * This copy from-TraceEventForBatch-to-string-array is required to + * avoid 2 things: + * -- Wire format where each trace event has the extra + * overhead/space of one long timestamp (the one used for + * DelayQueue.getDelay() ). + * -- More complicated wire format that will + * diminish the effectiveness of compression + * -- Requiring TraceEventForBatch .class to be in client JVM. + * This will avoid versioning conflicts. + * @param traceWriter + * @param sortedAndTypedEventList + * @throws IOException + */ + public int transmitBatch(ObjectOutputStream traceWriter, + List sortedAndTypedEventList, int burstSize) + throws IOException { + List eventsForSingleBurst = new ArrayList( + burstSize); + + int remaining = sortedAndTypedEventList.size(); + int burstCount = 0; + for (TraceEventForBatch tefb : sortedAndTypedEventList) { + eventsForSingleBurst.add( (String) tefb.getTraceEvent() ); + remaining--; + + if (eventsForSingleBurst.size() % burstSize == 0 + || remaining ==0 ) { + String[] aryEventsForSingleBurst = new String[eventsForSingleBurst.size()]; + eventsForSingleBurst.toArray(aryEventsForSingleBurst); + byte[] wireData = SerializationHelper.toWire(aryEventsForSingleBurst); + traceWriter.writeObject(wireData); + burstCount++; + eventsForSingleBurst.clear(); + } + } + System.out.println("stranded items [" + remaining + "]"); + return burstCount; + } + + public void gracefulShutdown() { + while (!outgoingData.isEmpty()) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // Ignore + } + } + } } diff --git a/org.intrace/src/org/intrace/output/trace/TraceHandler.java b/org.intrace/src/org/intrace/output/trace/TraceHandler.java index c3ce315..d6f2393 100644 --- a/org.intrace/src/org/intrace/output/trace/TraceHandler.java +++ b/org.intrace/src/org/intrace/output/trace/TraceHandler.java @@ -416,13 +416,25 @@ private static final Object STACK_ELE_DELIM = ","; StringBuilder sb = new StringBuilder(); int counter = 0; for(StackTraceElement ste : Thread.currentThread().getStackTrace() ) { - if ( ste.getClassName().indexOf(INTRACE_PACKAGE) <0 - && (ste.getClassName().indexOf(THREAD) < 0) && ste.getMethodName().indexOf(GET_STACK_TRACE)<0) { + boolean disqualify = false; + if (ste != null) { + + if ( (ste.getClassName() !=null) && + ( (ste.getClassName().indexOf(INTRACE_PACKAGE) >= 0) + || (ste.getClassName().indexOf(THREAD) >= 0) ) + ) + disqualify = true; + + if (ste.getMethodName()!=null + && ste.getMethodName().indexOf(GET_STACK_TRACE) >=0) + disqualify = true; + } + + if ( !disqualify) { if (counter++>0) sb.append(STACK_ELE_DELIM); //Just like Arrays.toString(), place a comma between each stack trace ele. sb.append(ste.toString()); } } - return sb.toString(); } @Override diff --git a/org.intrace/src/org/intrace/shared/AgentConfigConstants.java b/org.intrace/src/org/intrace/shared/AgentConfigConstants.java index b3fe9b2..d4826bc 100644 --- a/org.intrace/src/org/intrace/shared/AgentConfigConstants.java +++ b/org.intrace/src/org/intrace/shared/AgentConfigConstants.java @@ -16,7 +16,6 @@ public class AgentConfigConstants public static final String SERVER_PORT = "SERVER_PORT"; - public static final String GZIP = "[gzip-"; //Default is not enabled (false). public static final String CLASS_REGEX = "[regex-"; public static final String EXCLUDE_CLASS_REGEX = "[exclude-regex-"; public static final String INSTRU_ENABLED = "[instru-"; @@ -39,7 +38,6 @@ public class AgentConfigConstants COMMANDS.add(VERBOSE_MODE + ""); COMMANDS.add(OPT_SERVER_PORT + ""); COMMANDS.add(CALLBACK_PORT + ""); - COMMANDS.add(GZIP + ""); COMMANDS.add(EXIT_STACK_TRACE + ""); } } diff --git a/org.intrace/src/org/intrace/shared/Base64.java b/org.intrace/src/org/intrace/shared/Base64.java deleted file mode 100644 index c82ded0..0000000 --- a/org.intrace/src/org/intrace/shared/Base64.java +++ /dev/null @@ -1,2064 +0,0 @@ -package org.intrace.shared; -/** - *

Encodes and decodes to and from Base64 notation.

- *

Homepage: http://iharder.net/base64.

- * - *

Example:

- * - * String encoded = Base64.encode( myByteArray ); - *
- * byte[] myByteArray = Base64.decode( encoded ); - * - *

The options parameter, which appears in a few places, is used to pass - * several pieces of information to the encoder. In the "higher level" methods such as - * encodeBytes( bytes, options ) the options parameter can be used to indicate such - * things as first gzipping the bytes before encoding them, not inserting linefeeds, - * and encoding using the URL-safe and Ordered dialects.

- * - *

Note, according to RFC3548, - * Section 2.1, implementations should not add line feeds unless explicitly told - * to do so. I've got Base64 set to this behavior now, although earlier versions - * broke lines by default.

- * - *

The constants defined in Base64 can be OR-ed together to combine options, so you - * might make a call like this:

- * - * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES ); - *

to compress the data before encoding it and then making the output have newline characters.

- *

Also...

- * String encoded = Base64.encodeBytes( crazyString.getBytes() ); - * - * - * - *

- * Change Log: - *

- *
    - *
  • v2.3.7 - Fixed subtle bug when base 64 input stream contained the - * value 01111111, which is an invalid base 64 character but should not - * throw an ArrayIndexOutOfBoundsException either. Led to discovery of - * mishandling (or potential for better handling) of other bad input - * characters. You should now get an IOException if you try decoding - * something that has bad characters in it.
  • - *
  • v2.3.6 - Fixed bug when breaking lines and the final byte of the encoded - * string ended in the last column; the buffer was not properly shrunk and - * contained an extra (null) byte that made it into the string.
  • - *
  • v2.3.5 - Fixed bug in {@link #encodeFromFile} where estimated buffer size - * was wrong for files of size 31, 34, and 37 bytes.
  • - *
  • v2.3.4 - Fixed bug when working with gzipped streams whereby flushing - * the Base64.OutputStream closed the Base64 encoding (by padding with equals - * signs) too soon. Also added an option to suppress the automatic decoding - * of gzipped streams. Also added experimental support for specifying a - * class loader when using the - * {@link #decodeToObject(java.lang.String, int, java.lang.ClassLoader)} - * method.
  • - *
  • v2.3.3 - Changed default char encoding to US-ASCII which reduces the internal Java - * footprint with its CharEncoders and so forth. Fixed some javadocs that were - * inconsistent. Removed imports and specified things like java.io.IOException - * explicitly inline.
  • - *
  • v2.3.2 - Reduced memory footprint! Finally refined the "guessing" of how big the - * final encoded data will be so that the code doesn't have to create two output - * arrays: an oversized initial one and then a final, exact-sized one. Big win - * when using the {@link #encodeBytesToBytes(byte[])} family of methods (and not - * using the gzip options which uses a different mechanism with streams and stuff).
  • - *
  • v2.3.1 - Added {@link #encodeBytesToBytes(byte[], int, int, int)} and some - * similar helper methods to be more efficient with memory by not returning a - * String but just a byte array.
  • - *
  • v2.3 - This is not a drop-in replacement! This is two years of comments - * and bug fixes queued up and finally executed. Thanks to everyone who sent - * me stuff, and I'm sorry I wasn't able to distribute your fixes to everyone else. - * Much bad coding was cleaned up including throwing exceptions where necessary - * instead of returning null values or something similar. Here are some changes - * that may affect you: - *
      - *
    • Does not break lines, by default. This is to keep in compliance with - * RFC3548.
    • - *
    • Throws exceptions instead of returning null values. Because some operations - * (especially those that may permit the GZIP option) use IO streams, there - * is a possiblity of an java.io.IOException being thrown. After some discussion and - * thought, I've changed the behavior of the methods to throw java.io.IOExceptions - * rather than return null if ever there's an error. I think this is more - * appropriate, though it will require some changes to your code. Sorry, - * it should have been done this way to begin with.
    • - *
    • Removed all references to System.out, System.err, and the like. - * Shame on me. All I can say is sorry they were ever there.
    • - *
    • Throws NullPointerExceptions and IllegalArgumentExceptions as needed - * such as when passed arrays are null or offsets are invalid.
    • - *
    • Cleaned up as much javadoc as I could to avoid any javadoc warnings. - * This was especially annoying before for people who were thorough in their - * own projects and then had gobs of javadoc warnings on this file.
    • - *
    - *
  • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug - * when using very small files (~< 40 bytes).
  • - *
  • v2.2 - Added some helper methods for encoding/decoding directly from - * one file to the next. Also added a main() method to support command line - * encoding/decoding from one file to the next. Also added these Base64 dialects: - *
      - *
    1. The default is RFC3548 format.
    2. - *
    3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates - * URL and file name friendly format as described in Section 4 of RFC3548. - * http://www.faqs.org/rfcs/rfc3548.html
    4. - *
    5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates - * URL and file name friendly format that preserves lexical ordering as described - * in http://www.faqs.org/qa/rfcc-1940.html
    6. - *
    - * Special thanks to Jim Kellerman at http://www.powerset.com/ - * for contributing the new Base64 dialects. - *
  • - * - *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
  • - *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
  • - *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • - *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
  • - *
  • v1.4 - Added helper methods to read/write files.
  • - *
  • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
  • - *
  • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
  • - *
  • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
  • - *
  • v1.3.3 - Fixed I/O streams which were totally messed up.
  • - *
- * - *

- * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

- * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.3.7 - */ -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding in first bit. Value is one. */ - public final static int ENCODE = 1; - - - /** Specify decoding in first bit. Value is zero. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed in second bit. Value is two. */ - public final static int GZIP = 2; - - /** Specify that gzipped data should not be automatically gunzipped. */ - public final static int DONT_GUNZIP = 4; - - - /** Do break lines when encoding. Value is 8. */ - public final static int DO_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "US-ASCII"; - - - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** The 64 valid Base64 values. */ - /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ - private final static byte[] _STANDARD_ALPHABET = { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - -/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ - private final static byte[] _URL_SAFE_ALPHABET = { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' - }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - - -/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * I don't get the point of this technique, but someone requested it, - * and it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - private final static byte[] _ORDERED_ALPHABET = { - (byte)'-', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', - (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'_', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' - }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' - 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' - 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' - -9,-9,-9,-9,-9 // Decimal 123 - 127 - ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 - }; - - -/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getAlphabet( int options ) { - if ((options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_ALPHABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_ALPHABET; - } else { - return _STANDARD_ALPHABET; - } - } // end getAlphabet - - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) { - if( (options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_DECODABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_DECODABET; - } else { - return _STANDARD_DECODABET; - } - } // end getAlphabet - - - - /** Defeats instantiation. */ - private Base64(){} - - - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { - encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); - return b4; - } // end encode3to4 - - - /** - *

Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

- *

This is the lowest level of the encoding methods with - * all possible parameters.

- * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options ) { - - byte[] ALPHABET = getAlphabet( options ); - - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded ByteBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){ - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while( raw.hasRemaining() ){ - int rem = Math.min(3,raw.remaining()); - raw.get(raw3,0,rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); - encoded.put(enc4); - } // end input remaining - } - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded CharBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){ - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while( raw.hasRemaining() ){ - int rem = Math.min(3,raw.remaining()); - raw.get(raw3,0,rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS ); - for( int i = 0; i < 4; i++ ){ - encoded.put( (char)(enc4[i] & 0xFF) ); - } - } // end input remaining - } - - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @throws java.io.IOException if there is an error - * @throws NullPointerException if serializedObject is null - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - throws java.io.IOException { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * The object is not GZip-compressed before being encoded. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - throws java.io.IOException { - - if( serializableObject == null ){ - throw new NullPointerException( "Cannot serialize a null object." ); - } // end if: null - - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.util.zip.GZIPOutputStream gzos = null; - java.io.ObjectOutputStream oos = null; - - - try { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - if( (options & GZIP) != 0 ){ - // Gzip - gzos = new java.util.zip.GZIPOutputStream(b64os); - oos = new java.io.ObjectOutputStream( gzos ); - } else { - // Not gzipped - oos = new java.io.ObjectOutputStream( b64os ); - } - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue){ - // Fall back to some Java default - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @return The data in Base64-encoded form - * @throws NullPointerException if source array is null - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - *

As of v 2.3, if there is an error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @return The Base64-encoded data as a String - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes( source, off, len, NO_OPTIONS ); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { - byte[] encoded = encodeBytesToBytes( source, off, len, options ); - - // Return value according to relevant encoding. - try { - return new String( encoded, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) { - return new String( encoded ); - } // end catch - - } // end encodeBytes - - - - - /** - * Similar to {@link #encodeBytes(byte[])} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * - * @param source The data to convert - * @return The Base64-encoded data as a byte[] (of ASCII characters) - * @throws NullPointerException if source array is null - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes( byte[] source ) { - byte[] encoded = null; - try { - encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); - } catch( java.io.IOException ex ) { - assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); - } - return encoded; - } - - - /** - * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { - - if( source == null ){ - throw new NullPointerException( "Cannot serialize a null array." ); - } // end if: null - - if( off < 0 ){ - throw new IllegalArgumentException( "Cannot have negative offset: " + off ); - } // end if: off < 0 - - if( len < 0 ){ - throw new IllegalArgumentException( "Cannot have length offset: " + len ); - } // end if: len < 0 - - if( off + len > source.length ){ - throw new IllegalArgumentException( - String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); - } // end if: off < 0 - - - - // Compress? - if( (options & GZIP) != 0 ) { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - try { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - return baos.toByteArray(); - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else { - boolean breakLines = (options & DO_BREAK_LINES) != 0; - - //int len43 = len * 4 / 3; - //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - // Try to determine more precisely how big the array needs to be. - // If we get it right, we don't have to do an array copy, and - // we save a bunch of memory. - int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding - if( breakLines ){ - encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters - } - byte[] outBuff = new byte[ encLen ]; - - - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) { - encode3to4( source, d+off, 3, outBuff, e, options ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) { - encode3to4( source, d+off, len - d, outBuff, e, options ); - e += 4; - } // end if: some padding needed - - - // Only resize array if we didn't guess it right. - if( e <= outBuff.length - 1 ){ - // If breaking lines and the last byte falls right at - // the line length (76 bytes per line), there will be - // one extra byte, and the array will need to be resized. - // Not too bad of an estimate on array size, I'd say. - byte[] finalOut = new byte[e]; - System.arraycopy(outBuff,0, finalOut,0,e); - //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); - return finalOut; - } else { - //System.err.println("No need to resize array."); - return outBuff; - } - - } // end else: don't compress - - } // end encodeBytesToBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

This is the lowest level of the decoding methods with - * all possible parameters.

- * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @throws NullPointerException if source or destination arrays are null - * @throws IllegalArgumentException if srcOffset or destOffset are invalid - * or there is not enough room in the array. - * @since 1.3 - */ - private static int decode4to3( - byte[] source, int srcOffset, - byte[] destination, int destOffset, int options ) { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Source array was null." ); - } // end if - if( destination == null ){ - throw new NullPointerException( "Destination array was null." ); - } // end if - if( srcOffset < 0 || srcOffset + 3 >= source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); - } // end if - if( destOffset < 0 || destOffset +2 >= destination.length ){ - throw new IllegalArgumentException( String.format( - "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); - } // end if - - - byte[] DECODABET = getDecodabet( options ); - - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - } - } // end decodeToBytes - - - - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @return decoded data - * @since 2.3.1 - */ - public static byte[] decode( byte[] source ) - throws java.io.IOException { - byte[] decoded = null; -// try { - decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); -// } catch( java.io.IOException ex ) { -// assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); -// } - return decoded; - } - - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @param options Can specify options such as alphabet type to use - * @return decoded data - * @throws java.io.IOException If bogus characters exist in source data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len, int options ) - throws java.io.IOException { - - // Lots of error checking and exception throwing - if( source == null ){ - throw new NullPointerException( "Cannot decode null source array." ); - } // end if - if( off < 0 || off + len > source.length ){ - throw new IllegalArgumentException( String.format( - "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); - } // end if - - if( len == 0 ){ - return new byte[0]; - }else if( len < 4 ){ - throw new IllegalArgumentException( - "Base64-encoded string must have at least four characters, but length specified was " + len ); - } // end if - - byte[] DECODABET = getDecodabet( options ); - - int len34 = len * 3 / 4; // Estimate on array size - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; // Keep track of where we're writing - - byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space - int b4Posn = 0; // Keep track of four byte input buffer - int i = 0; // Source array counter - byte sbiDecode = 0; // Special value from DECODABET - - for( i = off; i < off+len; i++ ) { // Loop through source - - sbiDecode = DECODABET[ source[i]&0xFF ]; - - // White space, Equals sign, or legit Base64 character - // Note the values such as -5 and -9 in the - // DECODABETs at the top of the file. - if( sbiDecode >= WHITE_SPACE_ENC ) { - if( sbiDecode >= EQUALS_SIGN_ENC ) { - b4[ b4Posn++ ] = source[i]; // Save non-whitespace - if( b4Posn > 3 ) { // Time to decode? - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( source[i] == EQUALS_SIGN ) { - break; - } // end if: equals sign - } // end if: quartet built - } // end if: equals sign or better - } // end if: white space, equals sign or better - else { - // There's a bad input character in the Base64 stream. - throw new java.io.IOException( String.format( - "Bad Base64 input character decimal %d in array position %d", source[i]&0xFF, i ) ); - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @throws java.io.IOException If there is a problem - * @since 1.4 - */ - public static byte[] decode( String s ) throws java.io.IOException { - return decode( s, NO_OPTIONS ); - } - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if s is null - * @since 1.4 - */ - public static byte[] decode( String s, int options ) throws java.io.IOException { - - if( s == null ){ - throw new NullPointerException( "Input string was null." ); - } // end if - - byte[] bytes; - try { - bytes = s.getBytes( PREFERRED_ENCODING ); - } // end try - catch( java.io.UnsupportedEncodingException uee ) { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode( bytes, 0, bytes.length, options ); - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - boolean dontGunzip = (options & DONT_GUNZIP) != 0; - if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { - - int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) { - e.printStackTrace(); - // Just return originally-decoded bytes - } // end catch - finally { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 1.5 - */ - public static Object decodeToObject( String encodedObject ) - throws java.io.IOException, java.lang.ClassNotFoundException { - return decodeToObject(encodedObject,NO_OPTIONS,null); - } - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * If loader is not null, it will be the class loader - * used when deserializing. - * - * @param encodedObject The Base64 data to decode - * @param options Various parameters related to decoding - * @param loader Optional class loader to use in deserializing classes. - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 2.3.4 - */ - public static Object decodeToObject( - String encodedObject, int options, final ClassLoader loader ) - throws java.io.IOException, java.lang.ClassNotFoundException { - - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject, options ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try { - bais = new java.io.ByteArrayInputStream( objBytes ); - - // If no custom class loader is provided, use Java's builtin OIS. - if( loader == null ){ - ois = new java.io.ObjectInputStream( bais ); - } // end if: no loader provided - - // Else make a customized object input stream that uses - // the provided class loader. - else { - ois = new java.io.ObjectInputStream(bais){ - @Override - public Class resolveClass(java.io.ObjectStreamClass streamClass) - throws java.io.IOException, ClassNotFoundException { - Class c = Class.forName(streamClass.getName(), false, loader); - if( c == null ){ - return super.resolveClass(streamClass); - } else { - return c; // Class loader knows of this class. - } // end else: not null - } // end resolveClass - }; // end ois - } // end else: no custom class loader - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - catch( java.lang.ClassNotFoundException e ) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - finally { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if dataToEncode is null - * @since 2.1 - */ - public static void encodeToFile( byte[] dataToEncode, String filename ) - throws java.io.IOException { - - if( dataToEncode == null ){ - throw new NullPointerException( "Data to encode was null." ); - } // end iff - - Base64.OutputStream bos = null; - try { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static void decodeToFile( String dataToDecode, String filename ) - throws java.io.IOException { - - Base64.OutputStream bos = null; - try{ - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading encoded data - * @return decoded byte array - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - throws java.io.IOException { - - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { - length += numBytes; - } // end while - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading binary data - * @return base64-encoded string - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - throws java.io.IOException { - - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5) - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { - length += numBytes; - } // end while - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - /** - * Reads infile and encodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void encodeFileToFile( String infile, String outfile ) - throws java.io.IOException { - - String encoded = Base64.encodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end encodeFileToFile - - - /** - * Reads infile and decodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void decodeFileToFile( String infile, String outfile ) - throws java.io.IOException { - - byte[] decoded = Base64.decodeFromFile( infile ); - java.io.OutputStream out = null; - try{ - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream( outfile ) ); - out.write( decoded ); - } // end try - catch( java.io.IOException e ) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { out.close(); } - catch( Exception ex ){} - } // end finally - } // end decodeFileToFile - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream { - - private final boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private final byte[] buffer; // Small buffer holding converted data - private final int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private final boolean breakLines; // Break lines at less than 80 characters - private final int options; // Record options used to create the stream. - private final byte[] decodabet; // Local copies to avoid extra method calls - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) { - - super( in ); - this.options = options; // Record for later - this.breakLines = (options & DO_BREAK_LINES) > 0; - this.encode = (options & ENCODE) > 0; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - this.decodabet = getDecodabet(options); - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - @Override - public int read() throws java.io.IOException { - - // Do we need to get data? - if( position < 0 ) { - if( encode ) { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) { - b3[i] = (byte)b; - numBinaryBytes++; - } else { - break; // out of for loop - } // end else: end of stream - - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) { - encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); - position = 0; - numSigBytes = 4; - } // end if: got data - else { - return -1; // Must be end of stream - } // end else - } // end if: encoding - - // Else decoding - else { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) { - break; // Reads a -1 if end of stream - } // end if: end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) { - numSigBytes = decode4to3( b4, 0, buffer, 0, options ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ){ - return -1; - } // end if: got data - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { - lineLength = 0; - return '\n'; - } // end if - else { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) { - position = -1; - } // end if: end - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else { - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - @Override - public int read( byte[] dest, int off, int len ) - throws java.io.IOException { - int i; - int b; - for( i = 0; i < len; i++ ) { - b = read(); - - if( b >= 0 ) { - dest[off + i] = (byte) b; - } - else if( i == 0 ) { - return -1; - } - else { - break; // Out of 'for' loop - } // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream { - - private final boolean encode; - private int position; - private byte[] buffer; - private final int bufferLength; - private int lineLength; - private final boolean breakLines; - private final byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - private final int options; // Record for later - private final byte[] decodabet; // Local copies to avoid extra method calls - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) { - super( out ); - this.breakLines = (options & DO_BREAK_LINES) != 0; - this.encode = (options & ENCODE) != 0; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - this.options = options; - this.decodabet = getDecodabet(options); - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - @Override - public void write(int theByte) - throws java.io.IOException { - // Encoding suspended? - if( suspendEncoding ) { - this.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) { // Enough to encode. - - this.out.write( encode3to4( b4, buffer, bufferLength, options ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) { - this.out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else { - // Meaningful Base64 character? - if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) { // Enough to output. - - int len = Base64.decode4to3( buffer, 0, b4, 0, options ); - out.write( b4, 0, len ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - @Override - public void write( byte[] theBytes, int off, int len ) - throws java.io.IOException { - // Encoding suspended? - if( suspendEncoding ) { - this.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - * @throws java.io.IOException if there's an error. - */ - public void flushBase64() throws java.io.IOException { - if( position > 0 ) { - if( encode ) { - out.write( encode3to4( b4, buffer, position, options ) ); - position = 0; - } // end if: encoding - else { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - @Override - public void close() throws java.io.IOException { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @throws java.io.IOException if there's an error flushing - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 diff --git a/org.intrace/src/org/intrace/shared/SerializationHelper.java b/org.intrace/src/org/intrace/shared/SerializationHelper.java new file mode 100644 index 0000000..dd735e7 --- /dev/null +++ b/org.intrace/src/org/intrace/shared/SerializationHelper.java @@ -0,0 +1,46 @@ +package org.intrace.shared; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * Round trip serialization and gzip compression for an array of strings. + * This array is a list of intrace 'events' that are batched up for a single + * transmission over a socket. + * @author erikostermueller + * + */ +public class SerializationHelper { + + /** + * + * @param eventsForOneBurst + * @return + * @throws IOException + */ + public static byte[] toWire(String[] eventsForOneBurst) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzipOut = new GZIPOutputStream(baos); + ObjectOutputStream objectOut = new ObjectOutputStream(gzipOut); + objectOut.writeObject(eventsForOneBurst); + objectOut.close(); + byte[] bytes = baos.toByteArray(); + return bytes; + } + + public static String[] fromWire(byte[] bytes) throws IOException, ClassNotFoundException { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + GZIPInputStream gzipIn = new GZIPInputStream(bais); + ObjectInputStream objectIn = new ObjectInputStream(gzipIn); + String[] myObj1 = (String[]) objectIn.readObject(); + objectIn.close(); + return myObj1; + } + + +} diff --git a/org.intrace/testsrc/org/intracetest/agent/AgentTest.java b/org.intrace/testsrc/org/intracetest/agent/AgentTest.java index e3f8ae4..56e7cc9 100644 --- a/org.intrace/testsrc/org/intracetest/agent/AgentTest.java +++ b/org.intrace/testsrc/org/intracetest/agent/AgentTest.java @@ -21,7 +21,7 @@ import java.util.concurrent.LinkedBlockingQueue; import junit.framework.TestCase; import org.easymock.IAnswer; -import org.easymock.classextension.EasyMock; +import org.easymock.EasyMock; import org.intrace.output.AgentHelper; import org.intrace.output.IInstrumentationHandler; import org.intrace.shared.AgentConfigConstants; @@ -109,22 +109,6 @@ public class AgentTest extends TestCase assertEquals(helpResponse, expectedHelpResponse); } - /** - Intentionally, testSetting(AgentConfigConstants.GZIP, "true") does not work. - For simplicity's sake, this setting can only be configured as an intrace agent command line parameter -- [gzip-true - Note that there is no GUI checkbox to enabled Gzip. - To instead enable the client to change this, just change how ClassTransformer#getResponse() deals with a new gzip parameter. - The following simply confirms that -

    -
  • getsettings works for gzip parm
  • -
  • b) by default, GZIP is turned off.
  • -
- * - * @throws Exception - */ - public void testRetrievalOfGzipSetting() throws Exception { - testGetSetting(AgentConfigConstants.GZIP, "false"); - } public void testSettings() throws Exception { // Boolean settings @@ -149,6 +133,7 @@ public class AgentTest extends TestCase //testSetting(TraceConfigConstants.BRANCH, "false"); } + private BlockingQueue capturedTrace = null; public void testBranchPatterns() throws Throwable { // Create and init the mock @@ -160,7 +145,8 @@ public class AgentTest extends TestCase .andReturn(new HashMap()).anyTimes(); // Capture objects - final BlockingQueue capturedTrace = new LinkedBlockingQueue(); + //final BlockingQueue capturedTrace = new LinkedBlockingQueue(); + capturedTrace = new LinkedBlockingQueue(); IAnswer entryTraceWriter = new IAnswer() { @Override @@ -228,7 +214,6 @@ public class AgentTest extends TestCase // Setup agent testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); - testSetting(AgentConfigConstants.GZIP, "false"); testSetting(AgentConfigConstants.CLASS_REGEX, "BranchPatterns"); testSetting(AgentConfigConstants.VERBOSE_MODE, "false"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true"); @@ -344,7 +329,6 @@ public class AgentTest extends TestCase // Setup agent testSetting(AgentConfigConstants.INSTRU_ENABLED, "false"); - testSetting(AgentConfigConstants.GZIP, "false"); testSetting(AgentConfigConstants.CLASS_REGEX, "ArgumentTypes"); testSetting(AgentConfigConstants.VERBOSE_MODE, "false"); testSetting(AgentConfigConstants.SAVE_TRACED_CLASSFILES, "true"); diff --git a/org.intrace/testsrc/org/intracetest/agent/CompressionTest.java b/org.intrace/testsrc/org/intracetest/agent/CompressionTest.java deleted file mode 100644 index 19a25e5..0000000 --- a/org.intrace/testsrc/org/intracetest/agent/CompressionTest.java +++ /dev/null @@ -1,185 +0,0 @@ -package org.intracetest.agent; - -import java.io.IOException; - -import junit.framework.TestCase; - -import org.intrace.shared.Base64; -/** - -This test proves out the following API using text data from an intrace Event - -* String encoded = Base64.encode( myByteArray ); - *
- * byte[] myByteArray = Base64.decode( encoded ); - -
-
-				Plain ASCII		 Base64		Base64+GZIP
-				-----------------------------------------
-Tiny		   |	 70			   96			 116
-               |
-Small		   |	248			  332			 256
-               |
-Big     	   |   2306		     3076		  	 904
-               |
-Huge    	   |  12591		    16788		  	1036
-
-
-
- How small can we compress the InTrace event text for various sized InTrace events? -
    -
  • Base64 without GZIP was detrimental to small compression size in all cases
  • -
  • The 70 and 248 byte events were slightly larger with both Base64 and Base64+GZIP -- bad but a not horrible show stopper.
  • -
  • The Big event was more than 2x as small with Base64+GZIP -- good.
  • -
  • The Huge event was more than 10x as small with Base64+GZIP -- great.
  • -
  • -
- - Conclusions: - Will not use Base64 alone -- doesn't provide any compression with these samples. - Base64+GZIP doesn't hurt much with smaller event - - - - */ -public class CompressionTest extends TestCase -{ - private static final String ORIGINAL_TINY_INTRACE_EVENT = "[18:07:53.683]:[67]:org.hsqldb.jdbc.jdbcConnection:prepareStatement: {"; - private static final String ORIGINAL_SMALL_INTRACE_EVENT = "[18:07:53.683]:[67]:org.hsqldb.jdbc.jdbcConnection:prepareStatement" + - ": Return: org.hsqldb.jdbc.jdbcPreparedStatement@4b8efa2f[sql=" + - "[INSERT INTO Event (name, description, date, location) VALUES(?, ?, ?, ?)]" + - ", parameters=[[null], [null], [null], [null]]]"; - /** - * This isn't a real event (yet)...just adding some text to assess compression for larger messages. - */ - private static final String ORIGINAL_LARGE_STACK_TRACE = "[18:07:53.683]:[67]:org.hsqldb.jdbc.jdbcConnection:prepareStatement" + - ": Return: org.hsqldb.jdbc.jdbcPreparedStatement@4b8efa2f[sql=" + - "[INSERT INTO Event (name, description, date, location) VALUES(?, ?, ?, ?)]" + - ", parameters=[[null], [null], [null], [null]]]" + - "~java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "; - - /** - * This isn't a real event at all...just adding some text to assess compression for msg with more bytes. - */ - private static final String ORIGINAL_HUGE_STACK_TRACE = "[18:07:53.683]:[67]:org.hsqldb.jdbc.jdbcConnection:prepareStatement" + - ": Return: org.hsqldb.jdbc.jdbcPreparedStatement@4b8efa2f[sql=" + - "[INSERT INTO Event (name, description, date, location) VALUES(?, ?, ?, ?)]" + - ", parameters=[[null], [null], [null], [null]]]" + - "~java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "+ - "java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "+ - "java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "+ - "java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "+ - "java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "+ - "java.lang.Thread.getStackTrace(Thread.java:1567) example.webapp.servlet.HelloWorld.doGet(HelloWorld.java:38) javax.servlet.http.HttpServlet.service(HttpServlet.java:668) javax.servlet.http.HttpServlet.service(HttpServlet.java:770) org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669) org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137) org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560) org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231) org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072) org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382) org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193) org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006) org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135) org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116) org.eclipse.jetty.server.Server.handle(Server.java:365) org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485) org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53) org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926) org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988) org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635) org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235) org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72) org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264) org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608) org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543) java.lang.Thread.run(Thread.java:722) "; - - - public void testBasicCompressionAndDecompression_smallerWithGZip() throws IOException - { -// System.out.println("Original length of short data [" + ORIGINAL_SMALL_INTRACE_EVENT.length() + "]"); -// System.out.println("Original length of long data [" + ORIGINAL_LARGE_STACK_TRACE.length() + "]"); - byte[] eventData = ORIGINAL_SMALL_INTRACE_EVENT.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.GZIP); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.GZIP); - String reconstitutedString = new String(reconstitutedData); - - assertEquals("Size of original text", 248, ORIGINAL_SMALL_INTRACE_EVENT.length() ); - assertEquals("Size of compressed text", 256, compressedString.length() ); -// System.out.println("Compressed data [" + compressedString + "]"); -// System.out.println("Compressed with GZIP length - small data [" + compressedString.length() + "]"); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_SMALL_INTRACE_EVENT, reconstitutedString); - } - public void testBasicCompressionAndDecompression_largerWithGZip() throws IOException - { - byte[] eventData = ORIGINAL_LARGE_STACK_TRACE.getBytes(); - String compressedData = Base64.encodeBytes(eventData, Base64.GZIP); - //String compressedData = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - byte[] reconstitutedData = Base64.decode(compressedData, Base64.GZIP); - String reconstitutedString = new String(reconstitutedData); - - assertEquals("Size of original text", 2306, ORIGINAL_LARGE_STACK_TRACE.length() ); - assertEquals("Size of compressed text", 904, compressedData.length() ); - -// System.out.println("Compressed data [" + compressedData + "]"); -// System.out.println("Compressed with GZIP data - long data [" + compressedData.length() + "]"); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_LARGE_STACK_TRACE, reconstitutedString); - } - public void testBasicCompressionAndDecompression_smallerWithoutGZip() throws IOException - { - byte[] eventData = ORIGINAL_SMALL_INTRACE_EVENT.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.NO_OPTIONS); - String reconstitutedString = new String(reconstitutedData); - - - assertEquals("Size of original text", 248, ORIGINAL_SMALL_INTRACE_EVENT.length() ); - assertEquals("Size of compressed text", 332, compressedString.length() ); -// System.out.println("Compressed data [" + compressedString + "]"); -// System.out.println("Compressed without gzip data - small data [" + compressedString.length() + "]"); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_SMALL_INTRACE_EVENT, reconstitutedString); - } - public void testBasicCompressionAndDecompression_largerWithoutGZip() throws IOException - { - byte[] eventData = ORIGINAL_LARGE_STACK_TRACE.getBytes(); - String compressedData = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - //String compressedData = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - byte[] reconstitutedData = Base64.decode(compressedData, Base64.NO_OPTIONS); - String reconstitutedString = new String(reconstitutedData); - - assertEquals("Size of original text", 2306, ORIGINAL_LARGE_STACK_TRACE.length() ); - assertEquals("Size of compressed text", 3076, compressedData.length() ); - -// System.out.println("Compressed data [" + compressedData + "]"); -// System.out.println("Compressed without gzip - large data [" + compressedData.length() + "]"); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_LARGE_STACK_TRACE, reconstitutedString); - } - - public void testBasicCompressionAndDecompression_tinyWithGZip() throws IOException - { - byte[] eventData = ORIGINAL_TINY_INTRACE_EVENT.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.GZIP); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.GZIP); - String reconstitutedString = new String(reconstitutedData); - - - assertEquals("Size of original text", 70, ORIGINAL_TINY_INTRACE_EVENT.length() ); - assertEquals("Size of compressed text", 116, compressedString.length() ); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_TINY_INTRACE_EVENT, reconstitutedString); - } - public void testBasicCompressionAndDecompression_tinyWithoutGZip() throws IOException - { - byte[] eventData = ORIGINAL_TINY_INTRACE_EVENT.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.NO_OPTIONS); - String reconstitutedString = new String(reconstitutedData); - - - assertEquals("Size of original text", 70, ORIGINAL_TINY_INTRACE_EVENT.length() ); - assertEquals("Size of compressed text", 96, compressedString.length() ); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_TINY_INTRACE_EVENT, reconstitutedString); - } - public void testBasicCompressionAndDecompression_hugeWithGZip() throws IOException - { - byte[] eventData = ORIGINAL_HUGE_STACK_TRACE.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.GZIP); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.GZIP); - String reconstitutedString = new String(reconstitutedData); - - - assertEquals("Size of original text", 12591, ORIGINAL_HUGE_STACK_TRACE.length() ); - assertEquals("Size of compressed text", 1036, compressedString.length() ); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_HUGE_STACK_TRACE, reconstitutedString); - } - public void testBasicCompressionAndDecompression_hugeWithoutGZip() throws IOException - { - byte[] eventData = ORIGINAL_HUGE_STACK_TRACE.getBytes(); - String compressedString = Base64.encodeBytes(eventData, Base64.NO_OPTIONS); - byte[] reconstitutedData = Base64.decode(compressedString, Base64.NO_OPTIONS); - String reconstitutedString = new String(reconstitutedData); - - - assertEquals("Size of original text", 12591, ORIGINAL_HUGE_STACK_TRACE.length() ); - assertEquals("Size of compressed text", 16788, compressedString.length() ); - assertEquals("Unable to compress and decompress intrace event data", ORIGINAL_HUGE_STACK_TRACE, reconstitutedString); - } -} diff --git a/org.intrace/testsrc/org/intracetest/agent/DelayQueueBatchTest.java b/org.intrace/testsrc/org/intracetest/agent/DelayQueueBatchTest.java new file mode 100644 index 0000000..0108794 --- /dev/null +++ b/org.intrace/testsrc/org/intracetest/agent/DelayQueueBatchTest.java @@ -0,0 +1,123 @@ +package org.intracetest.agent; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.Delayed; + +import org.intrace.output.BatchScheduler; +import org.intrace.output.IBatchSchedulerConfig; +import org.junit.Test; + +public class DelayQueueBatchTest { + + @Test + public void canCalculateDepartureTimes_currentInterval() { + + final int INTERVAL = 250; // I N T E R V A L =======> Z E R O t o 2 4 9 + final int DEPARTURE_TIME_MILLIS = INTERVAL; + IBatchSchedulerConfig config1 = new IBatchSchedulerConfig() { + @Override + public int getDrainInterval() {return INTERVAL;} + @Override + public int getDrainIntervalMultiplier() { return 1; } + }; + + //First milli of the interval + BatchScheduler batchScheduler = new BatchScheduler(0L, config1); + assertEquals("Departure time was not calculated correctcly", DEPARTURE_TIME_MILLIS, batchScheduler.getDepartureTimeMillis() ); + + //Last milli of the interval + batchScheduler = new BatchScheduler( (INTERVAL-1), config1); + assertEquals("Departure time was not calculated correctcly", DEPARTURE_TIME_MILLIS, batchScheduler.getDepartureTimeMillis() ); + + //First milli of subsequent interval + batchScheduler = new BatchScheduler( INTERVAL, config1); + assertEquals("Departure time was not calculated correctcly", 2*DEPARTURE_TIME_MILLIS, batchScheduler.getDepartureTimeMillis() ); + + } + @Test + public void canCalculateDepartureTimes_subsequentInterval() { + + final int INTERVAL = 250; // I N T E R V A L =======> Z E R O t o 2 4 9 + final int DEPARTURE = INTERVAL; + final int MULTIPLIER = 2; + IBatchSchedulerConfig config1 = new IBatchSchedulerConfig() { + @Override + public int getDrainInterval() {return INTERVAL;} + @Override + public int getDrainIntervalMultiplier() { return MULTIPLIER; } + }; + + BatchScheduler batchScheduler = new BatchScheduler(0L, config1); + assertEquals("Departure time was not calculated correctcly", MULTIPLIER*DEPARTURE, batchScheduler.getDepartureTimeMillis() ); + + batchScheduler = new BatchScheduler( (INTERVAL-1), config1); + assertEquals("Departure time was not calculated correctcly", MULTIPLIER*DEPARTURE, batchScheduler.getDepartureTimeMillis() ); + + batchScheduler = new BatchScheduler( INTERVAL, config1); + assertEquals("Departure time was not calculated correctcly", MULTIPLIER*DEPARTURE+INTERVAL, batchScheduler.getDepartureTimeMillis() ); + + } + + IBatchSchedulerConfig config2 = new IBatchSchedulerConfig() { + @Override + public int getDrainInterval() {return 1000;} + @Override + public int getDrainIntervalMultiplier() { return 2; } + }; + /** + * First num goes in one batch, 2nd two numbers go in the subsequent batch. + */ + @Test + public void canCalculateSubsequentBatchedDepartureTimes() { + BatchScheduler batchScheduler = new BatchScheduler(2352819999L, this.config2); + assertEquals("Departure time was not calculated correctcly", 2352821000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2352820000L, this.config2); + assertEquals("Departure time was not calculated correctcly", 2352822000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2352820001L, this.config2); + assertEquals("Departure time was not calculated correctcly", 2352822000L, batchScheduler.getDepartureTimeMillis() ); + + } + + @Test + public void canCalculateBatchedDepartureTimes() { + BatchScheduler batchScheduler = new BatchScheduler(899079228, this.config2); + assertEquals("Departure time was not calculated correctcly", 899081000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(899079473L, this.config2); + assertEquals("Departure time was not calculated correctcly", 899081000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2293479377L, this.config2); + assertEquals("Departure time was not calculated correctcly", 2293481000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(902521357L, this.config2); + assertEquals("Departure time was not calculated correctcly", 902523000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(902756592L, this.config2); + assertEquals("Departure time was not calculated correctcly", 902758000L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2294063215L, this.config2); + assertEquals("Departure time was not calculated correctcly", 2294065000L, batchScheduler.getDepartureTimeMillis() ); + + } + /** + * First num goes in one batch, 2nd two numbers go in the subsequent batch. + * + + + + */ + @Test + public void canCalculateSubsequentBatchedDepartureTimes_smallerInterval() { + IBatchSchedulerConfig config1 = new IBatchSchedulerConfig() { + @Override + public int getDrainInterval() {return 250;} + @Override + public int getDrainIntervalMultiplier() { return 2; } + }; + + BatchScheduler batchScheduler = new BatchScheduler(2352819999L, config1); + assertEquals("Departure time was not calculated correctcly", 2352820250L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2352820000L, config1); + assertEquals("Departure time was not calculated correctcly", 2352820500L, batchScheduler.getDepartureTimeMillis() ); + batchScheduler = new BatchScheduler(2352820001L, config1); + assertEquals("Departure time was not calculated correctcly", 2352820500L, batchScheduler.getDepartureTimeMillis() ); + + } + +} diff --git a/org.intrace/testsrc/org/intracetest/agent/DelayQueueProofOfConceptTest.java b/org.intrace/testsrc/org/intracetest/agent/DelayQueueProofOfConceptTest.java new file mode 100644 index 0000000..ec296d8 --- /dev/null +++ b/org.intrace/testsrc/org/intracetest/agent/DelayQueueProofOfConceptTest.java @@ -0,0 +1,164 @@ +package org.intracetest.agent; + + +import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.Delayed; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.TimeUnit; +import org.junit.Test; +import org.junit.Before; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.atomic.AtomicLong; + + + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.DelayQueue; + +import org.intrace.output.trace.TraceHandler; +import static org.junit.Assert.assertEquals; + +/** This is a 'proof of concept' test that demonstrates how to use a DelayQueue to emit + * data in batches, instead of individual queue items. Only jdk code and test code is exercised here. + * + * This test sticks 9 items into a DelayQueue. These items comes out in 3 batches. + * The delays (in milliseconds) between the batches are defined in the offsetA, offsetB, and offsetC variables. + */ +public class DelayQueueProofOfConceptTest +{ + public static final int POISON_PILL = 3; + private static long offsetA = 53L; + private static long offsetB = 109L; + private static long offsetC = 150L; + private static long baseline = 0; + BlockingQueue pizzaQueue = new DelayQueue(); + PizzaEaterRunnable pizzaEater = new PizzaEaterRunnable(pizzaQueue); + List expectedArrivalTimestamps = new ArrayList(); + @Before + public void setup() throws java.lang.InterruptedException { + baseline = System.currentTimeMillis(); + + /** + * B A T C H # 1 + */ + pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) ); + pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) ); + pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) ); + pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) ); + pizzaQueue.put( createTypeOne(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetA) ); + /** + * B A T C H # 2 + */ + pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) ); + pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) ); + pizzaQueue.put( createTypeTwo(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetB) ); + /** + * B A T C H # 3 + */ + + pizzaQueue.put( createTypeThree(baseline) ); expectedArrivalTimestamps.add( new Long(baseline+offsetC) ); + } + private Pizza createTypeOne(long baseline) { + Pizza newPizza = new Pizza( baseline + offsetA, 1 ); + return newPizza; + } + + private Pizza createTypeTwo(long baseline) { + Pizza newPizza = new Pizza( baseline + offsetB, 2 ); + return newPizza; + } + private Pizza createTypeThree(long baseline) { + Pizza newPizza = new Pizza( baseline + offsetC, POISON_PILL ); + return newPizza; + } + @Test + public void testDelayQueueBurst() { + try { + PizzaEaterRunnable eater = new PizzaEaterRunnable(pizzaQueue);//queue was filled in setup method + Thread t = new Thread(eater); + t.start(); + //Time will elapse as DelayQueue emits + //a three bursts of pizzas from the queue, 1 second between each burst. + //The thread will finish when a 'poison pill' is detected by the thread. + t.join(); + boolean compare = eater.arrivalTimestamps.equals(expectedArrivalTimestamps); + String actual = eater.arrivalTimestamps.toString(); + String expected = expectedArrivalTimestamps.toString(); + assertTrue("did not get expected arrival times. Expected [" + expected + "] Actual [" + actual + "]",compare); + + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + public static class Pizza implements Delayed { + + private long departureTimestampMillis = 0L; + private int type = 0; + public Pizza(long val, int type) { + this.departureTimestampMillis = val; + this.type = type; + System.out.println("New Pizza [" + this.toString() + "]"); + } + public String toString() { + return "Pizza {departureTimestampMillis=" + this.departureTimestampMillis + ",type=" + this.type + "}"; + } + public int getType() { + return this.type; + } + @Override + public long getDelay(TimeUnit unit) { + long ts = System.currentTimeMillis(); + //System.out.println("current [" + ts + "] " + this.toString()) ; + return this.departureTimestampMillis - ts; + } + + @Override + public int compareTo(Delayed o) { + long rc = this.getDelay( TimeUnit.MILLISECONDS ) - o.getDelay( TimeUnit.MILLISECONDS ); + return (int)rc; + } + } + class PizzaEaterRunnable implements Runnable { + private static final int batchSize = 1000000;//Only concerned with huge/horrible events, otherwise monitoring is required to tune batch size. + public List arrivalTimestamps = new ArrayList(); + BlockingQueue pizzaQueue; + public volatile boolean stopRequested = false; + public PizzaEaterRunnable(BlockingQueue val) { + this.pizzaQueue = val; + } + private void eatMany(List manyPizzas) { + for(Pizza pizza : manyPizzas) + eat(pizza); + } + private void eat(Pizza pizza) { + arrivalTimestamps.add( new Long(System.currentTimeMillis()) ); + if (pizza.getType() == DelayQueueProofOfConceptTest.POISON_PILL) { + this.stopRequested = true; + } + } + + @Override + public void run() { + List manyPizzas = new ArrayList(); + while (!stopRequested) { + try { + //System.out.println(Thread.currentThread().getName() + " eating up bread "); + Pizza pizza = pizzaQueue.poll(100, TimeUnit.MILLISECONDS); + if (pizza !=null) { + manyPizzas.add(pizza); + pizzaQueue.drainTo(manyPizzas,batchSize); + eatMany(manyPizzas); + manyPizzas.clear(); + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } +} + diff --git a/org.intrace/testsrc/org/intracetest/agent/SerializationTest.java b/org.intrace/testsrc/org/intracetest/agent/SerializationTest.java new file mode 100644 index 0000000..bd0579c --- /dev/null +++ b/org.intrace/testsrc/org/intracetest/agent/SerializationTest.java @@ -0,0 +1,222 @@ +package org.intracetest.agent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.util.ArrayList; +import java.util.List; + +import org.intrace.shared.SerializationHelper; +import org.intrace.output.NetworkDataSenderThread.TraceEventForBatch; +import org.intrace.output.NetworkDataSenderThread; +import org.junit.Before; +import org.junit.Test; + +public class SerializationTest { + + String[] myData = { "foo", "bar" }; + String sampleEvent = "[00:48:55.797]:[19]:org.hsqldb.jdbc.jdbcStatement:: }~org.hsqldb.jdbc.jdbcStatement.(Unknown Source),org.hsqldb.jdbc.jdbcConnection.createStatement(Unknown Source),org.apache.commons.dbcp.DelegatingConnection.createStatement(DelegatingConnection.java:257),org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createStatement(PoolingDataSource.java:216),org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:389),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:455),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:463),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:471),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:476),org.springframework.jdbc.core.JdbcTemplate.queryForInt(JdbcTemplate.java:485),example.webapp.dao.JdbcEventDAO.countAll(JdbcEventDAO.java:64),sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43),java.lang.reflect.Method.invoke(Method.java:606),org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309),org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150),org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172),org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202),com.sun.proxy.$Proxy6.countAll(Unknown Source),example.webapp.servlet.HelloExecuteQuery.doGet(HelloExecuteQuery.java:25),javax.servlet.http.HttpServlet.service(HttpServlet.java:668),javax.servlet.http.HttpServlet.service(HttpServlet.java:770),org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669),org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137),org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560),org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231),org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072),org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382),org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193),org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135),org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116),org.eclipse.jetty.server.Server.handle(Server.java:365),org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485),org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53),org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926),org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988),org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635),org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235),org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72),org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264),org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608),org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)"; + @Before + public void setup() { + sampleEvents[0] = "[00:48:55.797]:[19]:org.hsqldb.jdbc.jdbcStatement:: }~org.hsqldb.jdbc.jdbcStatement.(Unknown Source),org.hsqldb.jdbc.jdbcConnection.createStatement(Unknown Source),org.apache.commons.dbcp.DelegatingConnection.createStatement(DelegatingConnection.java:257),org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createStatement(PoolingDataSource.java:216),org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:389),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:455),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:463),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:471),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:476),org.springframework.jdbc.core.JdbcTemplate.queryForInt(JdbcTemplate.java:485),example.webapp.dao.JdbcEventDAO.countAll(JdbcEventDAO.java:64),sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43),java.lang.reflect.Method.invoke(Method.java:606),org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309),org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150),org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172),org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202),com.sun.proxy.$Proxy6.countAll(Unknown Source),example.webapp.servlet.HelloExecuteQuery.doGet(HelloExecuteQuery.java:25),javax.servlet.http.HttpServlet.service(HttpServlet.java:668),javax.servlet.http.HttpServlet.service(HttpServlet.java:770),org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669),org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137),org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560),org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231),org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072),org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382),org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193),org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135),org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116),org.eclipse.jetty.server.Server.handle(Server.java:365),org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485),org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53),org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926),org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988),org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635),org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235),org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72),org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264),org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608),org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)"; + sampleEvents[1] = "[00:48:55.797]:[19]:org.apache.commons.dbcp.DelegatingStatement:: {:61"; + sampleEvents[2] = "[00:48:55.797]:[19]:org.apache.commons.dbcp.DelegatingStatement:: Arg (c): jdbc:hsqldb:mem:event, UserName=SA, HSQL Database Engine Driver"; + sampleEvents[3] = "[00:48:55.797]:[19]:org.apache.commons.dbcp.DelegatingStatement:: Arg (s): org.hsqldb.jdbc.jdbcStatement@2300de71"; + sampleEvents[4] = "[00:48:55.798]:[19]:org.apache.commons.dbcp.DelegatingStatement:: }:64~org.apache.commons.dbcp.DelegatingStatement.(DelegatingStatement.java:64),org.apache.commons.dbcp.DelegatingConnection.createStatement(DelegatingConnection.java:257),org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createStatement(PoolingDataSource.java:216),org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:389),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:455),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:463),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:471),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:476),org.springframework.jdbc.core.JdbcTemplate.queryForInt(JdbcTemplate.java:485),example.webapp.dao.JdbcEventDAO.countAll(JdbcEventDAO.java:64),sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43),java.lang.reflect.Method.invoke(Method.java:606),org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309),org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150),org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172),org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202),com.sun.proxy.$Proxy6.countAll(Unknown Source),example.webapp.servlet.HelloExecuteQuery.doGet(HelloExecuteQuery.java:25),javax.servlet.http.HttpServlet.service(HttpServlet.java:668),javax.servlet.http.HttpServlet.service(HttpServlet.java:770),org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669),org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137),org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560),org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231),org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072),org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382),org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193),org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135),org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116),org.eclipse.jetty.server.Server.handle(Server.java:365),org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485),org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53),org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926),org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988),org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635),org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235),org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72),org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264),org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608),org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)"; + sampleEvents[5] = "[00:48:55.798]:[19]:org.apache.commons.dbcp.DelegatingStatement:: {:61"; + sampleEvents[6] = "[00:48:55.798]:[19]:org.apache.commons.dbcp.DelegatingStatement:: Arg (c): jdbc:hsqldb:mem:event, UserName=SA, HSQL Database Engine Driver"; + sampleEvents[7] = "[00:48:55.799]:[19]:org.apache.commons.dbcp.DelegatingStatement:: Arg (s): org.hsqldb.jdbc.jdbcStatement@2300de71"; + sampleEvents[8] = "[00:48:55.799]:[19]:org.apache.commons.dbcp.DelegatingStatement:: }:64~org.apache.commons.dbcp.DelegatingStatement.(DelegatingStatement.java:64),org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createStatement(PoolingDataSource.java:216),org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:389),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:455),org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:463),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:471),org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:476),org.springframework.jdbc.core.JdbcTemplate.queryForInt(JdbcTemplate.java:485),example.webapp.dao.JdbcEventDAO.countAll(JdbcEventDAO.java:64),sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method),sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57),sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43),java.lang.reflect.Method.invoke(Method.java:606),org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309),org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150),org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110),org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172),org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202),com.sun.proxy.$Proxy6.countAll(Unknown Source),example.webapp.servlet.HelloExecuteQuery.doGet(HelloExecuteQuery.java:25),javax.servlet.http.HttpServlet.service(HttpServlet.java:668),javax.servlet.http.HttpServlet.service(HttpServlet.java:770),org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:669),org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:455),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137),org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:560),org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231),org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1072),org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:382),org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193),org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1006),org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135),org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116),org.eclipse.jetty.server.Server.handle(Server.java:365),org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:485),org.eclipse.jetty.server.BlockingHttpConnection.handleRequest(BlockingHttpConnection.java:53),org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:926),org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:988),org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:635),org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235),org.eclipse.jetty.server.BlockingHttpConnection.handle(BlockingHttpConnection.java:72),org.eclipse.jetty.server.bio.SocketConnector$ConnectorEndPoint.run(SocketConnector.java:264),org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608),org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)"; + sampleEvents[9] = "[00:48:55.799]:[19]:org.apache.commons.dbcp.DelegatingStatement:executeQuery: {:206"; + } + + @Test + public void canSerializeAndDeserializeEvents() throws IOException, ClassNotFoundException { + + byte[] myBytes = SerializationHelper.toWire(this.myData); + assertEquals("Didn't find right size of data-on-the-wire.", 75, myBytes.length) ; + + String[] surprise = SerializationHelper.fromWire(myBytes); + + assertEquals("Could not find 1st element in deserialized array of strings","foo",surprise[0]); + assertEquals("Could not find 2nd element in deserialized array of strings","bar",surprise[1]); + } + /** + * These are typical times for serializing/compressing about 12mb of events + * on my 2.3Ghz i7 MacBook + * Serialize time [199] + * Deserialize time [171] + + * @throws IOException + * @throws ClassNotFoundException + */ + @Test + public void canCalculateSizeOfDataSerializedAndCompressed() throws IOException, ClassNotFoundException { + + List myEvents = new ArrayList(); + int unCompressedByteCount = 0; + for(int i = 0; i < 10000;i++) { + long timestamp = System.currentTimeMillis(); + Long longTimestamp = new Long(timestamp); + String tmp = + longTimestamp.toString() //Throw in some randomness, more realism for the compression algo. + + sampleEvents [ i % 10 ] + + Long.toHexString(2*timestamp) ; + unCompressedByteCount+=tmp.length(); //Throw in some randomness, more realism for the compression algo. + + myEvents.add(tmp); + } + assertTrue("Did not find correct total size for 10k uncompressed strings ",unCompressedByteCount > 12000000 ); + assertTrue("Did not find correct total size for 10k uncompressed strings ",unCompressedByteCount < 12500000 ); + + String[] myEventsArray;// = new String[myEvents.size()]; + myEventsArray = myEvents.toArray(new String[0]); + assertEquals("Unable to create array from list", 10000,myEventsArray.length); + + long start = System.currentTimeMillis(); + byte[] myBytes = SerializationHelper.toWire(myEventsArray); + + long finish = System.currentTimeMillis(); + System.out.println("Serialize time [" + (finish-start) + "]"); + + assertTrue("Did not find correct size for String array compressed into a byte array",myBytes.length > 80000); + assertTrue("Did not find correct size for String array compressed into a byte array",myBytes.length < 90000); + + start = System.currentTimeMillis(); + String[] surprise = SerializationHelper.fromWire(myBytes); + finish = System.currentTimeMillis(); + System.out.println("Deserialize time [" + (finish-start) + "]"); + + } + @Test + public void canSendOneBurstsOfEvents() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream o = new ObjectOutputStream(baos); + + List events = new ArrayList(); + events.add( new TraceEventForBatch("foo",System.currentTimeMillis(), 1)); + events.add( new TraceEventForBatch("bar",System.currentTimeMillis(), 2)); + + NetworkDataSenderThread sender = new NetworkDataSenderThread(null,null); + int numBursts = sender.transmitBatch( o, events, 10 ); + assertEquals("Sent just two events with a larger burst size. should have sent exactly 1 burst.", 1, numBursts); + //o.close(); + + byte[] compressedObjects = baos.toByteArray() ; + assertTrue( "didn't find enough raw compressed data", compressedObjects.length > 4);//surely the data has to be bigger than 4 bytes, right? + + ByteArrayInputStream bais = new ByteArrayInputStream(compressedObjects); + ObjectInputStream ois = new ObjectInputStream(bais); + Object objectFromWire = ois.readObject(); + ois.close(); + if (objectFromWire instanceof byte[]) { + byte[] freshFromWire = (byte[]) objectFromWire; + assertEquals("Didn't find size of byte array on wire", 75, freshFromWire.length); + + String results[] = SerializationHelper.fromWire( freshFromWire ); + + + assertEquals( "Didn't find right count of String in the array after sending bursts", 2, results.length ); + + assertEquals( "Couldn't receive multiple bursts with just two events", "foo", results[0] ); + assertEquals( "Couldn't receive multiple bursts with just two events", "bar", results[1] ); + } else { + fail("Didn't serialize right kind of object"); + } + + + } + + @Test + public void canSendThreeBurstsOfEvents() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream o = new ObjectOutputStream(baos); + + List events = new ArrayList(); + events.add( new TraceEventForBatch("yankee",System.currentTimeMillis(), 1)); + events.add( new TraceEventForBatch("doodle",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("went",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("to",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("town", System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("riding",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("on",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("a",System.currentTimeMillis(), 2)); + events.add( new TraceEventForBatch("pony",System.currentTimeMillis(), 2)); + + NetworkDataSenderThread sender = new NetworkDataSenderThread(null,null); + int numBursts = sender.transmitBatch( o, events, 3 ); + assertEquals("Sent just 9 events with a burst size of 3. should have sent exactly 3 burst.", 3, numBursts); + //o.close(); + + byte[] compressedObjects = baos.toByteArray() ; + assertTrue( "didn't find enough raw compressed data", compressedObjects.length > 4);//surely the data has to be bigger than 4 bytes, right? + + ByteArrayInputStream bais = new ByteArrayInputStream(compressedObjects); + ObjectInputStream ois = new ObjectInputStream(bais); + Object objectFromWire1 = ois.readObject(); + Object objectFromWire2 = ois.readObject(); + Object objectFromWire3 = ois.readObject(); + ois.close(); + if (objectFromWire1 instanceof byte[]) { + byte[] freshFromWire = (byte[]) objectFromWire1; + assertEquals("Didn't find size of byte array on wire", 87, freshFromWire.length); + + String results[] = SerializationHelper.fromWire( freshFromWire ); + + + assertEquals( "Didn't find right count of String in the array after sending bursts", 3, results.length ); + + assertEquals( "Couldn't receive multiple bursts with just 9 events", "yankee", results[0] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "doodle", results[1] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "went", results[2] ); + } else { + fail("Didn't serialize right kind of object"); + } + + if (objectFromWire2 instanceof byte[]) { + byte[] freshFromWire = (byte[]) objectFromWire2; + assertEquals("Didn't find size of byte array on wire", 84, freshFromWire.length); + + String results[] = SerializationHelper.fromWire( freshFromWire ); + + + assertEquals( "Didn't find right count of String in the array for the 2nd burst", 3, results.length ); + + assertEquals( "Couldn't receive multiple bursts with just 9 events", "to", results[0] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "town", results[1] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "riding", results[2] ); + } else { + fail("Didn't serialize right kind of object"); + } + if (objectFromWire1 instanceof byte[]) { + byte[] freshFromWire = (byte[]) objectFromWire3; + assertEquals("Didn't find size of byte array on wire", 80, freshFromWire.length); + + String results[] = SerializationHelper.fromWire( freshFromWire ); + + + assertEquals( "Didn't find right count of String in the array for the 3rd burst", 3, results.length ); + + assertEquals( "Couldn't receive multiple bursts with just 9 events", "on", results[0] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "a", results[1] ); + assertEquals( "Couldn't receive multiple bursts with just 9 events", "pony", results[2] ); + } else { + fail("Didn't serialize right kind of object"); + } + + + + } + + + private String[] sampleEvents = new String[10]; + + + +}