You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by ph...@apache.org on 2010/07/21 20:15:09 UTC

svn commit: r966342 - in /avro/trunk: ./ lang/java/ lang/java/src/java/org/apache/avro/ipc/stats/ lang/java/src/test/java/org/apache/avro/ipc/stats/ share/

Author: philz
Date: Wed Jul 21 18:15:08 2010
New Revision: 966342

URL: http://svn.apache.org/viewvc?rev=966342&view=rev
Log:
AVRO-587. Add Charts and Templating to Stats View 
(Contributed by Patrick Wendell)


Modified:
    avro/trunk/CHANGES.txt
    avro/trunk/lang/java/build.xml
    avro/trunk/lang/java/ivy.xml
    avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java
    avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java
    avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java
    avro/trunk/share/rat-excludes.txt

Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Wed Jul 21 18:15:08 2010
@@ -33,6 +33,9 @@ Avro 1.4.0 (unreleased)
     implementation. (Harry Wang via cutting)
 
   IMPROVEMENTS
+    AVRO-587. Add Charts and Templating to Stats View
+    (Patrick Wendell via philz)
+
     AVRO-584. Update Histogram for Stats Plugin
     (Patrick Wendell via philz)
 

Modified: avro/trunk/lang/java/build.xml
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/build.xml?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/build.xml (original)
+++ avro/trunk/lang/java/build.xml Wed Jul 21 18:15:08 2010
@@ -41,11 +41,16 @@
   <property name="java.src.dir" value="${src.dir}/java"/>  	
   <property name="build.dir" value="${basedir}/build"/>
   <property name="lib.dir" value="${basedir}/lib"/>
+  <property name="template.dir" value="${src.dir}/java/org/apache/avro/ipc/stats/templates"/>
+  <property name="static.dir" value="${src.dir}/java/org/apache/avro/ipc/stats/static"/>
+
 
   <property name="build.classes" value="${build.dir}/classes"/>
   <property name="build.doc" value="${build.dir}/doc"/>
   <property name="build.javadoc" value="${build.doc}/api/java"/>
   <property name="build.javadoc.log" value="${build.dir}/javadoc.log"/>
+  <property name="build.template.dir" value="${build.classes}/org/apache/avro/ipc/stats/templates"/>
+  <property name="build.static.dir" value="${build.classes}/org/apache/avro/ipc/stats/static"/>
 
   <property name="test.count" value="100"/>
   <property name="test.junit.output.format" value="plain"/>
@@ -169,6 +174,12 @@
       <fileset file="${basedir}/../../NOTICE.txt"/>
       <fileset file="${basedir}/../../share/VERSION.txt"/>
     </copy>
+    <copy todir="${build.template.dir}">
+      <fileset dir="${template.dir}" includes="**/**"/>
+    </copy>
+    <copy todir="${build.static.dir}">
+      <fileset dir="${static.dir}" includes="**/**"/>
+    </copy>
   </target>
 
   <target name="ivy-download" unless="ivy.jar.exists" depends="init">

Modified: avro/trunk/lang/java/ivy.xml
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/ivy.xml?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/ivy.xml (original)
+++ avro/trunk/lang/java/ivy.xml Wed Jul 21 18:15:08 2010
@@ -45,6 +45,7 @@
         rev="2.2"/>
     <dependency org="org.mortbay.jetty" name="jetty"
         rev="6.1.22"/>
+    <dependency org="org.apache.velocity" name="velocity" rev="1.6.4"/>
     <dependency org="junit" name="junit" rev="4.8.1" conf="test->default"/>
     <dependency org="checkstyle" name="checkstyle" rev="5.0"
         conf="test->default"/>

Modified: avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java (original)
+++ avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java Wed Jul 21 18:15:08 2010
@@ -17,8 +17,11 @@
  */
 package org.apache.avro.ipc.stats;
 
+import java.nio.ByteBuffer;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 import java.util.concurrent.ConcurrentHashMap;
@@ -32,14 +35,15 @@ import org.apache.avro.ipc.stats.Stopwat
 
 /**
  * Collects count and latency statistics about RPC calls.  Keeps
- * data for every method.
+ * data for every method. Can be added to a Requestor (client)
+ * or Responder (server). 
  *
  * This uses milliseconds as the standard unit of measure
  * throughout the class, stored in floats.
  */
 public class StatsPlugin extends RPCPlugin {
   /** Static declaration of histogram buckets. */
-  static final Segmenter<String, Float> DEFAULT_SEGMENTER =
+  static final Segmenter<String, Float> LATENCY_SEGMENTER =
     new Histogram.TreeMapSegmenter<Float>(new TreeSet<Float>(Arrays.asList(
             0f,
            25f,
@@ -57,28 +61,74 @@ public class StatsPlugin extends RPCPlug
         60000f, // 1 minute
        600000f)));
 
+  static final Segmenter<String, Integer> PAYLOAD_SEGMENTER =
+    new Histogram.TreeMapSegmenter<Integer>(new TreeSet<Integer>(Arrays.asList(
+            0,
+           25,
+           50,
+           75,
+          100,
+          200,
+          300,
+          500,
+          750,
+         1000, // 1 k
+         2000,
+         5000,
+        10000,
+        50000, 
+       100000)));
+  
   /** Per-method histograms.
-   * Must be accessed while holding a lock on methodTimings. */
+   * Must be accessed while holding a lock. */
   Map<Message, FloatHistogram<?>> methodTimings =
     new HashMap<Message, FloatHistogram<?>>();
 
+  Map<Message, IntegerHistogram<?>> sendPayloads =
+    new HashMap<Message, IntegerHistogram<?>>();
+  
+  Map<Message, IntegerHistogram<?>> receivePayloads =
+    new HashMap<Message, IntegerHistogram<?>>();
+  
   /** RPCs in flight. */
   ConcurrentMap<RPCContext, Stopwatch> activeRpcs =
     new ConcurrentHashMap<RPCContext, Stopwatch>();
   private Ticks ticks;
 
-  private Segmenter<?, Float> segmenter;
+  /** How long I've been alive */
+  public Date startupTime = new Date();
+  
+  private Segmenter<?, Float> floatSegmenter;
+  private Segmenter<?, Integer> integerSegmenter;
 
   /** Construct a plugin with custom Ticks and Segmenter implementations. */
-  StatsPlugin(Ticks ticks, Segmenter<?, Float> segmenter) {
-    this.segmenter = segmenter;
+  StatsPlugin(Ticks ticks, Segmenter<?, Float> floatSegmenter, 
+      Segmenter<?, Integer> integerSegmenter) {
+    this.floatSegmenter = floatSegmenter;
+    this.integerSegmenter = integerSegmenter;
     this.ticks = ticks;
   }
 
   /** Construct a plugin with default (system) ticks, and default
    * histogram segmentation. */
   public StatsPlugin() {
-    this(Stopwatch.SYSTEM_TICKS, DEFAULT_SEGMENTER);
+    this(Stopwatch.SYSTEM_TICKS, LATENCY_SEGMENTER, PAYLOAD_SEGMENTER);
+  }
+  
+  /**
+   * Helper to get the size of an RPC payload.
+   */
+  private int getPayloadSize(List<ByteBuffer> payload) {
+    if (payload == null) {
+      return 0;
+    }
+    
+    int size = 0;
+    for (ByteBuffer bb: payload) {
+      size = size + bb.limit();
+    }
+    
+    return size;
   }
 
   @Override
@@ -86,15 +136,65 @@ public class StatsPlugin extends RPCPlug
     Stopwatch t = new Stopwatch(ticks);
     t.start();
     this.activeRpcs.put(context, t);
+    
+    synchronized(receivePayloads) {
+      IntegerHistogram<?> h = receivePayloads.get(context.getMessage());
+      if (h == null) {
+        h = createNewIntegerHistogram();
+        receivePayloads.put(context.getMessage(), h);
+      }
+      h.add(getPayloadSize(context.getRequestPayload()));
+    }
   }
-
+  
   @Override
   public void serverSendResponse(RPCContext context) {
     Stopwatch t = this.activeRpcs.remove(context);
     t.stop();
     publish(context, t);
+    
+    synchronized(sendPayloads) {
+      IntegerHistogram<?> h = sendPayloads.get(context.getMessage());
+      if (h == null) {
+        h = createNewIntegerHistogram();
+        sendPayloads.put(context.getMessage(), h);
+      }
+      h.add(getPayloadSize(context.getResponsePayload()));
+    }
   }
-
+  
+  @Override
+  public void clientSendRequest(RPCContext context) {
+    Stopwatch t = new Stopwatch(ticks);
+    t.start();
+    this.activeRpcs.put(context, t);
+    
+    synchronized(sendPayloads) {
+      IntegerHistogram<?> h = sendPayloads.get(context.getMessage());
+      if (h == null) {
+        h = createNewIntegerHistogram();
+       sendPayloads.put(context.getMessage(), h);
+      }
+      h.add(getPayloadSize(context.getRequestPayload()));
+    }
+  }
+  
+  @Override
+  public void clientReceiveResponse(RPCContext context) {
+    Stopwatch t = this.activeRpcs.remove(context);
+    t.stop();
+    publish(context, t);
+    
+    synchronized(receivePayloads) {
+      IntegerHistogram<?> h = receivePayloads.get(context.getMessage());
+      if (h == null) {
+        h = createNewIntegerHistogram();
+        receivePayloads.put(context.getMessage(), h);
+      }
+      h.add(getPayloadSize(context.getRequestPayload()));
+    }
+  }
+  
   /** Adds timing to the histograms. */
   private void publish(RPCContext context, Stopwatch t) {
     Message message = context.getMessage();
@@ -102,7 +202,7 @@ public class StatsPlugin extends RPCPlug
     synchronized(methodTimings) {
       FloatHistogram<?> h = methodTimings.get(context.getMessage());
       if (h == null) {
-        h = createNewHistogram();
+        h = createNewFloatHistogram();
         methodTimings.put(context.getMessage(), h);
       }
       h.add(nanosToMillis(t.elapsedNanos()));
@@ -110,10 +210,15 @@ public class StatsPlugin extends RPCPlug
   }
 
   @SuppressWarnings("unchecked")
-  private FloatHistogram<?> createNewHistogram() {
-    return new FloatHistogram(segmenter);
+  private FloatHistogram<?> createNewFloatHistogram() {
+    return new FloatHistogram(floatSegmenter);
   }
 
+  @SuppressWarnings("unchecked")
+  private IntegerHistogram<?> createNewIntegerHistogram() {
+    return new IntegerHistogram(integerSegmenter);
+  }
+  
   /** Converts nanoseconds to milliseconds. */
   static float nanosToMillis(long elapsedNanos) {
     return elapsedNanos / 1000000.0f;

Modified: avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java (original)
+++ avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java Wed Jul 21 18:15:08 2010
@@ -19,15 +19,33 @@ package org.apache.avro.ipc.stats;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
 import java.util.Map.Entry;
 
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
 
 import org.apache.avro.Protocol.Message;
 import org.apache.avro.ipc.RPCContext;
+import org.mortbay.jetty.servlet.DefaultServlet;
+import org.mortbay.resource.Resource;
 
 /**
  * Exposes information provided by a StatsPlugin as
@@ -36,71 +54,217 @@ import org.apache.avro.ipc.RPCContext;
  * This class follows the same synchronization conventions
  * as StatsPlugin, to avoid requiring StatsPlugin to serve
  * a copy of the data.
- */
+ */ 
 public class StatsServlet extends HttpServlet {
   private final StatsPlugin statsPlugin;
+  private VelocityEngine velocityEngine;
+  private static final SimpleDateFormat FORMATTER = 
+    new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
 
-  public StatsServlet(StatsPlugin statsPlugin) {
+  public StatsServlet(StatsPlugin statsPlugin) throws UnavailableException {
     this.statsPlugin = statsPlugin;
+    this.velocityEngine = new VelocityEngine();
+    
+    // These two properties tell Velocity to use its own classpath-based loader
+    velocityEngine.addProperty("resource.loader", "class");
+    velocityEngine.addProperty("class.resource.loader.class",
+        "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+  }
+  
+  /* Helper class to store per-message data which is passed to templates.
+   * 
+   * The template expects a list of charts, each of which is parameterized by
+   * map key-value string attributes. */
+  public class RenderableMessage { // Velocity brakes if not public
+    public String name;
+    public int numCalls;
+    public ArrayList<HashMap<String, String>> charts;
+    
+    public RenderableMessage(String name) {
+      this.name = name;
+      this.charts = new ArrayList<HashMap<String, String>>();
+    }
+    
+    public ArrayList<HashMap<String, String>> getCharts() {
+      return this.charts;
+    }
+    
+    public String getname() {
+      return this.name;
+    }
+    
+    public int getNumCalls() {
+      return this.numCalls;
+    }
   }
 
+  /* Surround each string in an array with
+   * quotation marks and escape existing quotes.
+   * 
+   * This is useful when we have an array of strings that we want to turn into
+   * a javascript array declaration. 
+   */
+  protected static List<String> escapeStringArray(List<String> input) {
+    for (int i = 0; i < input.size(); i++) {
+      input.set(i, "\"" + input.get(i).replace("\"", "\\\"") + "\"");
+    }
+    return input;
+  }
+  
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp)
       throws ServletException, IOException {
     resp.setContentType("text/html");
-    writeStats(resp.getWriter());
+    String url = req.getRequestURL().toString();
+    String[] parts = url.split("//")[1].split("/");
+    
+    try {
+      writeStats(resp.getWriter()); 
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+    }
   }
 
   void writeStats(Writer w) throws IOException {
-    w.append("<html><head><title>Avro RPC Stats</title></head>");
-    w.append("<body><h1>Avro RPC Stats</h1>");
-
-    w.append("<h2>Active RPCs</h2>");
-    w.append("<ol>");
-    for (Entry<RPCContext, Stopwatch> rpc : this.statsPlugin.activeRpcs.entrySet()) {
-      writeActiveRpc(w, rpc.getKey(), rpc.getValue());
+    VelocityContext context = new VelocityContext();
+    context.put("title", "Avro RPC Stats"); 
+    
+    ArrayList<String> rpcs = new ArrayList<String>();  // in flight rpcs
+    
+    ArrayList<RenderableMessage> messages = 
+      new ArrayList<RenderableMessage>();
+    
+    for (Entry<RPCContext, Stopwatch> rpc : 
+         this.statsPlugin.activeRpcs.entrySet()) {
+      rpcs.add(renderActiveRpc(rpc.getKey(), rpc.getValue()));
     }
-    w.append("</ol>");
-
-    w.append("<h2>Per-method Timing</h2>");
+    
+    // Get set of all seen messages
+    Set<Message> keys = null;
     synchronized(this.statsPlugin.methodTimings) {
-      for (Entry<Message, FloatHistogram<?>> e :
-        this.statsPlugin.methodTimings.entrySet()) {
-        writeMethod(w, e.getKey(), e.getValue());
+       keys = this.statsPlugin.methodTimings.keySet();
+    
+      for (Message m: keys) {
+        messages.add(renderMethod(m));
       }
     }
-    w.append("</body></html>");
+    
+    context.put("inFlightRpcs", rpcs);
+    context.put("messages", messages);
+    
+    context.put("currTime", FORMATTER.format(new Date()));
+    context.put("startupTime", FORMATTER.format(statsPlugin.startupTime));
+    
+    Template t;
+    try {
+      t = velocityEngine.getTemplate(
+          "org/apache/avro/ipc/stats/templates/statsview.vm");
+    } catch (ResourceNotFoundException e) {
+      throw new IOException();
+    } catch (ParseErrorException e) {
+      throw new IOException();
+    } catch (Exception e) {
+      throw new IOException();
+    }
+    t.merge(context, w);
   }
 
-  private void writeActiveRpc(Writer w, RPCContext rpc, Stopwatch stopwatch) throws IOException {
-    w.append("<li>").append(rpc.getMessage().getName()).append(": ");
-    w.append(formatMillis(StatsPlugin.nanosToMillis(stopwatch.elapsedNanos())));
-    w.append("</li>");
-  }
-
-  private void writeMethod(Writer w, Message message, FloatHistogram<?> hist) throws IOException {
-    w.append("<h3>").append(message.getName()).append("</h3>");
-    w.append("<p>Number of calls: ");
-    w.append(Integer.toString(hist.getCount()));
-    w.append("</p><p>Average Duration: ");
-    w.append(formatMillis(hist.getMean()));
-    w.append("</p>");
-    w.append("</p><p>Std Dev: ");
-    w.append(formatMillis(hist.getUnbiasedStdDev()));
-    w.append("</p>");
-
-    w.append("<dl>");
-
-    for (Histogram.Entry<?> e : hist.entries()) {
-      w.append("<dt>");
-      w.append(e.bucket.toString());
-      w.append("</dt>");
-      w.append("<dd>").append(Integer.toString(e.count)).append("</dd>");
-      w.append("</dt>");
-    }
-    w.append("</dl>");
+  private String renderActiveRpc(RPCContext rpc, Stopwatch stopwatch) 
+      throws IOException {
+    String out = new String();
+    out += rpc.getMessage().getName() + ": " + 
+        formatMillis(StatsPlugin.nanosToMillis(stopwatch.elapsedNanos()));
+    return out;
   }
 
+  
+  private RenderableMessage renderMethod(Message message) {
+    RenderableMessage out = new RenderableMessage(message.getName());
+    
+    synchronized(this.statsPlugin.methodTimings) {
+      FloatHistogram<?> hist = this.statsPlugin.methodTimings.get(message);
+      out.numCalls = hist.getCount();
+      
+      HashMap<String, String> latencyBar = new HashMap<String, String>();
+      // Fill in chart attributes for velocity
+      latencyBar.put("type", "bar");
+      latencyBar.put("title", "All-Time Latency");
+      latencyBar.put("units", "ms");
+      latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+      latencyBar.put("avg", Float.toString(hist.getMean()));
+      latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+      latencyBar.put("labelStr", 
+          Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+      latencyBar.put("boundaryStr",
+          Arrays.toString(escapeStringArray(hist.getSegmenter().
+              getBucketLabels()).toArray()));
+      latencyBar.put("dataStr", Arrays.toString(hist.getHistogram())); 
+      out.charts.add(latencyBar);
+      
+      HashMap<String, String> latencyDot = new HashMap<String, String>();
+      latencyDot.put("title", "Latency");
+      latencyDot.put("type", "dot");
+      latencyDot.put("dataStr", 
+          Arrays.toString(hist.getRecentAdditions().toArray()));
+      out.charts.add(latencyDot);
+    }
+    
+    synchronized(this.statsPlugin.sendPayloads) {
+      IntegerHistogram<?> hist = this.statsPlugin.sendPayloads.get(message);
+      HashMap<String, String> latencyBar = new HashMap<String, String>();
+      // Fill in chart attributes for velocity
+      latencyBar.put("type", "bar");
+      latencyBar.put("title", "All-Time Send Payload");
+      latencyBar.put("units", "ms");
+      latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+      latencyBar.put("avg", Float.toString(hist.getMean()));
+      latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+      latencyBar.put("labelStr", 
+          Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+      latencyBar.put("boundaryStr",
+          Arrays.toString(escapeStringArray(hist.getSegmenter().
+              getBucketLabels()).toArray()));
+      latencyBar.put("dataStr", Arrays.toString(hist.getHistogram())); 
+      out.charts.add(latencyBar);
+      
+      HashMap<String, String> latencyDot = new HashMap<String, String>();
+      latencyDot.put("title", "Send Payload");
+      latencyDot.put("type", "dot");
+      latencyDot.put("dataStr", 
+          Arrays.toString(hist.getRecentAdditions().toArray()));
+      out.charts.add(latencyDot);
+    }
+    
+    synchronized(this.statsPlugin.receivePayloads) {
+      IntegerHistogram<?> hist = this.statsPlugin.receivePayloads.get(message);
+      HashMap<String, String> latencyBar = new HashMap<String, String>();
+      // Fill in chart attributes for velocity
+      latencyBar.put("type", "bar");
+      latencyBar.put("title", "All-Time Receive Payload");
+      latencyBar.put("units", "ms");
+      latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+      latencyBar.put("avg", Float.toString(hist.getMean()));
+      latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+      latencyBar.put("labelStr", 
+          Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+      latencyBar.put("boundaryStr",
+          Arrays.toString(escapeStringArray(hist.getSegmenter().
+              getBucketLabels()).toArray()));
+      latencyBar.put("dataStr", Arrays.toString(hist.getHistogram())); 
+      out.charts.add(latencyBar);
+      
+      HashMap<String, String> latencyDot = new HashMap<String, String>();
+      latencyDot.put("title", "Recv Payload");
+      latencyDot.put("type", "dot");
+      latencyDot.put("dataStr", 
+          Arrays.toString(hist.getRecentAdditions().toArray()));
+      out.charts.add(latencyDot);
+    }
+    
+    return out;
+  }
+  
   private CharSequence formatMillis(float millis) {
     return String.format("%.0fms", millis);
   }

Modified: avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java (original)
+++ avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java Wed Jul 21 18:15:08 2010
@@ -22,6 +22,11 @@ import static org.junit.Assert.assertTru
 
 import java.io.IOException;
 import java.io.StringWriter;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import javax.servlet.UnavailableException;
 
 import org.apache.avro.Protocol;
 import org.apache.avro.Protocol.Message;
@@ -31,14 +36,12 @@ import org.apache.avro.generic.GenericRe
 import org.apache.avro.generic.GenericResponder;
 import org.apache.avro.ipc.AvroRemoteException;
 import org.apache.avro.ipc.HttpServer;
+import org.apache.avro.ipc.HttpTransceiver;
 import org.apache.avro.ipc.LocalTransceiver;
 import org.apache.avro.ipc.RPCContext;
 import org.apache.avro.ipc.Responder;
 import org.apache.avro.ipc.Transceiver;
 import org.junit.Test;
-import org.mortbay.jetty.Server;
-import org.mortbay.jetty.servlet.Context;
-import org.mortbay.jetty.servlet.ServletHolder;
 import org.mortbay.log.Log;
 
 public class TestStatsPluginAndServlet {
@@ -53,9 +56,18 @@ public class TestStatsPluginAndServlet {
   /** Returns an HTML string. */
   private String generateServletResponse(StatsPlugin statsPlugin)
       throws IOException {
-    StatsServlet servlet = new StatsServlet(statsPlugin);
+    StatsServlet servlet;
+    try {
+      servlet = new StatsServlet(statsPlugin);
+    } catch (UnavailableException e1) {
+      throw new IOException();
+    }
     StringWriter w = new StringWriter();
-    servlet.writeStats(w);
+    try {
+      servlet.writeStats(w);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
     String o = w.toString();
     return o;
   }
@@ -95,13 +107,14 @@ public class TestStatsPluginAndServlet {
     }
 
     String o = generateServletResponse(statsPlugin);
-    assertTrue(o.contains("Number of calls: 10"));
+    assertTrue(o.contains("10 calls"));
   }
 
   @Test
   public void testMultipleRPCs() throws IOException {
     FakeTicks t = new FakeTicks();
-    StatsPlugin statsPlugin = new StatsPlugin(t, StatsPlugin.DEFAULT_SEGMENTER);
+    StatsPlugin statsPlugin = new StatsPlugin(t, StatsPlugin.LATENCY_SEGMENTER,
+        StatsPlugin.PAYLOAD_SEGMENTER);
     RPCContext context1 = makeContext();
     RPCContext context2 = makeContext();
     statsPlugin.serverReceiveRequest(context1);
@@ -114,11 +127,23 @@ public class TestStatsPluginAndServlet {
     statsPlugin.serverSendResponse(context1);
     t.passTime(900*MS); // second takes 900ms
     statsPlugin.serverSendResponse(context2);
-
     r = generateServletResponse(statsPlugin);
-    assertTrue(r.contains("Average Duration: 500ms"));
+    assertTrue(r.contains("Average: 500.0ms"));
   }
 
+  @Test
+  public void testPayloadSize() throws IOException {
+    Responder r = new TestResponder(protocol);
+    StatsPlugin statsPlugin = new StatsPlugin();
+    r.addRPCPlugin(statsPlugin);
+    Transceiver t = new LocalTransceiver(r);
+    makeRequest(t);
+    
+    String resp = generateServletResponse(statsPlugin);
+    assertTrue(resp.contains("Average: 2.0"));
+ 
+  }
+  
   private RPCContext makeContext() {
     RPCContext context = new RPCContext();
     context.setMessage(message);
@@ -144,7 +169,8 @@ public class TestStatsPluginAndServlet {
   }
 
   /**
-   * Demo program for using RPC stats.  Tool can be used (as below)
+   * Demo program for using RPC stats. This automatically generates
+   * client RPC requests. Alternatively a can be used (as below)
    * to trigger RPCs.
    * <pre>
    * java -jar build/avro-tools-*.jar rpcsend '{"protocol":"sleepy","namespace":null,"types":[],"messages":{"sleep":{"request":[{"name":"millis","type":"long"}],"response":"null"}}}' sleep localhost 7002 '{"millis": 20000}'
@@ -158,7 +184,8 @@ public class TestStatsPluginAndServlet {
     }
     Protocol protocol = Protocol.parse("{\"protocol\": \"sleepy\", "
         + "\"messages\": { \"sleep\": {"
-        + "   \"request\": [{\"name\": \"millis\", \"type\": \"long\"}], "
+        + "   \"request\": [{\"name\": \"millis\", \"type\": \"long\"}," +
+          "{\"name\": \"data\", \"type\": \"bytes\"}], "
         + "   \"response\": \"null\"} } }");
     Log.info("Using protocol: " + protocol.toString());
     Responder r = new SleepyResponder(protocol);
@@ -169,11 +196,24 @@ public class TestStatsPluginAndServlet {
     HttpServer avroServer = new HttpServer(r, Integer.parseInt(args[0]));
     avroServer.start();
 
-    // Ideally we could use the same Jetty server
-    Server httpServer = new Server(Integer.parseInt(args[1]));
-    new Context(httpServer, "/").addServlet(
-        new ServletHolder(new StatsServlet(p)), "/*");
-    httpServer.start();
-    httpServer.join();
+    StatsServer ss = new StatsServer(p, 8080);
+   
+    HttpTransceiver trans = new HttpTransceiver(
+        new URL("http://localhost:" + Integer.parseInt(args[0])));
+    GenericRequestor req = new GenericRequestor(protocol, trans); 
+    
+    
+    while(true) {
+      Thread.sleep(1000);
+      GenericRecord params = new GenericData.Record(protocol.getMessages().get(
+        "sleep").getRequest());
+      Random rand = new Random();
+      params.put("millis", Math.abs(rand.nextLong()) % 1000);
+      int payloadSize = Math.abs(rand.nextInt()) % 10000;
+      byte[] payload = new byte[payloadSize];
+      rand.nextBytes(payload);
+      params.put("data", ByteBuffer.wrap(payload));
+      req.request("sleep", params);
+    }
   }
 }

Modified: avro/trunk/share/rat-excludes.txt
URL: http://svn.apache.org/viewvc/avro/trunk/share/rat-excludes.txt?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/share/rat-excludes.txt (original)
+++ avro/trunk/share/rat-excludes.txt Wed Jul 21 18:15:08 2010
@@ -30,3 +30,5 @@ lang/c/jansson/**
 lang/c/src/queue.h
 lang/c/src/st.h
 lang/c/src/st.c
+lang/java/src/java/org/apache/avro/ipc/stats/static/*.js
+lang/java/src/java/org/apache/avro/ipc/stats/static/*.css