[htrace] 03/09: Imported Upstream version 3.1.0

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Tue Aug 25 14:11:51 UTC 2015


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to branch master
in repository htrace.

commit 2930f56cc98210b4bc025a6c84b4d74f7a477091
Author: Emmanuel Bourg <ebourg at apache.org>
Date:   Tue Jul 28 13:54:46 2015 +0200

    Imported Upstream version 3.1.0
---
 .gitignore                                         |    6 +
 BUILDING.txt                                       |   18 +
 DISCLAIMER.txt                                     |    7 +
 LICENSE.txt                                        |   46 +
 NOTICE.txt                                         |   12 +
 README.md                                          |  228 +-
 htrace-core/pom.xml                                |  108 +-
 htrace-core/src/go/BUILDING.txt                    |   31 +
 htrace-core/src/go/Godeps/Godeps.json              |   26 +
 htrace-core/src/go/format.sh                       |   40 +
 htrace-core/src/go/gobuild.sh                      |  116 +
 .../go/src/org/apache/htrace/bundler/bundler.go    |  228 ++
 .../src/go/src/org/apache/htrace/common/rest.go    |   29 +
 .../src/go/src/org/apache/htrace/common/span.go    |   90 +
 .../go/src/org/apache/htrace/common/span_test.go   |   66 +
 .../go/src/org/apache/htrace/common/test_util.go   |   74 +
 .../src/go/src/org/apache/htrace/conf/config.go    |  201 ++
 .../go/src/org/apache/htrace/conf/config_keys.go   |   62 +
 .../go/src/org/apache/htrace/conf/config_test.go   |  121 ++
 .../src/go/src/org/apache/htrace/conf/xml.go       |   61 +
 .../src/go/src/org/apache/htrace/htrace/cmd.go     |  160 ++
 .../go/src/org/apache/htrace/htraced/datastore.go  |  444 ++++
 .../org/apache/htrace/htraced/datastore_test.go    |  144 ++
 .../go/src/org/apache/htrace/htraced/htraced.go    |   37 +
 .../src/org/apache/htrace/htraced/mini_htraced.go  |  118 ++
 .../src/go/src/org/apache/htrace/htraced/rest.go   |  163 ++
 .../go/src/org/apache/htrace/resource/catalog.go   |   29 +
 .../src/go/src/org/apache/htrace/test/random.go    |   72 +
 .../{ => apache}/htrace/HTraceConfiguration.java   |   27 +-
 .../java/org/{ => apache}/htrace/NullScope.java    |    2 +-
 .../main/java/org/{ => apache}/htrace/Sampler.java |   29 +-
 .../java/org/apache/htrace/SamplerBuilder.java     |   83 +
 .../main/java/org/{ => apache}/htrace/Span.java    |   59 +-
 .../java/org/{ => apache}/htrace/SpanReceiver.java |   23 +-
 .../org/apache/htrace/SpanReceiverBuilder.java     |  126 ++
 .../{ => apache}/htrace/TimelineAnnotation.java    |    2 +-
 .../main/java/org/{ => apache}/htrace/Trace.java   |   10 +-
 .../java/org/{ => apache}/htrace/TraceInfo.java    |    2 +-
 .../java/org/{ => apache}/htrace/TraceScope.java   |    2 +-
 .../src/main/java/org/apache/htrace/TraceTree.java |  161 ++
 .../main/java/org/{ => apache}/htrace/Tracer.java  |    4 +-
 .../{ => apache}/htrace/impl/AlwaysSampler.java    |   12 +-
 .../org/{ => apache}/htrace/impl/CountSampler.java |   13 +-
 .../htrace/impl/LocalFileSpanReceiver.java         |   32 +-
 .../java/org/apache/htrace/impl/MilliSpan.java     |  337 +++
 .../org/{ => apache}/htrace/impl/NeverSampler.java |   12 +-
 .../{ => apache}/htrace/impl/POJOSpanReceiver.java |   17 +-
 .../htrace/impl/ProbabilitySampler.java            |   17 +-
 .../htrace/impl/StandardOutSpanReceiver.java       |   12 +-
 .../htrace/impl/TrueIfTracingSampler.java          |   14 +-
 .../htrace/wrappers/TraceCallable.java             |    8 +-
 .../htrace/wrappers/TraceExecutorService.java      |    2 +-
 .../{ => apache}/htrace/wrappers/TraceProxy.java   |    8 +-
 .../htrace/wrappers/TraceRunnable.java             |   12 +-
 .../src/main/java/org/htrace/TraceTree.java        |   95 -
 .../src/main/java/org/htrace/impl/MilliSpan.java   |  161 --
 .../org/{ => apache}/htrace/TestCountSampler.java  |   11 +-
 .../java/org/{ => apache}/htrace/TestHTrace.java   |   43 +-
 .../htrace/TestHTraceConfiguration.java            |    3 +-
 .../java/org/{ => apache}/htrace/TestSampler.java  |   28 +-
 .../org/apache/htrace/TestSpanReceiverBuilder.java |  122 ++
 .../java/org/{ => apache}/htrace/TraceCreator.java |    8 +-
 .../java/org/apache/htrace/impl/TestMilliSpan.java |  155 ++
 htrace-core/src/web/index.html                     |   24 +
 htrace-core/src/web/nested/nested.html             |   24 +
 htrace-flume/README.md                             |   76 +
 {htrace-zipkin => htrace-flume}/pom.xml            |   50 +-
 .../org/apache/htrace/impl/FlumeSpanReceiver.java  |  283 +++
 .../apache/htrace/impl/TestFlumeSpanReceiver.java  |  176 ++
 htrace-hbase/README.md                             |  166 ++
 htrace-hbase/pom.xml                               |  199 ++
 htrace-hbase/spans.png                             |  Bin 0 -> 105888 bytes
 .../apache/htrace/HBaseHTraceConfiguration.java    |   52 +
 .../org/apache/htrace/HBaseSpanReceiverHost.java   |  105 +
 .../org/apache/htrace/impl/HBaseSpanReceiver.java  |  359 ++++
 .../htrace/protobuf/generated/SpanProtos.java      | 2241 ++++++++++++++++++++
 .../org/apache/htrace/viewer/HBaseSpanViewer.java  |  226 ++
 .../htrace/viewer/HBaseSpanViewerServer.java       |  115 +
 .../htrace/viewer/HBaseSpanViewerSpansServlet.java |   97 +
 .../viewer/HBaseSpanViewerTracesServlet.java       |   84 +
 htrace-hbase/src/main/protobuf/Span.proto          |   38 +
 .../src/main/webapps/htrace/WEB-INF/web.xml        |   16 +
 htrace-hbase/src/main/webapps/htrace/index.html    |   24 +
 htrace-hbase/src/main/webapps/htrace/spans.html    |   28 +
 htrace-hbase/src/main/webapps/htrace/spans.js      |  166 ++
 htrace-hbase/src/main/webapps/htrace/traces.html   |   28 +
 htrace-hbase/src/main/webapps/htrace/traces.js     |   62 +
 .../main/webapps/static/bootstrap-theme.min.css    |   10 +
 .../src/main/webapps/static/bootstrap.min.css      |    9 +
 .../java/org/apache/htrace/impl/HBaseTestUtil.java |   79 +
 .../apache/htrace/impl/TestHBaseSpanReceiver.java  |  228 ++
 .../apache/htrace/viewer/TestHBaseSpanViewer.java  |   97 +
 htrace-hbase/timelines.png                         |  Bin 0 -> 67279 bytes
 htrace-hbase/traces.png                            |  Bin 0 -> 4112 bytes
 htrace-zipkin/pom.xml                              |   16 +-
 .../java/com/twitter/zipkin/gen/Annotation.java    |  687 ------
 .../com/twitter/zipkin/gen/AnnotationType.java     |   60 -
 .../com/twitter/zipkin/gen/BinaryAnnotation.java   |  721 -------
 .../main/java/com/twitter/zipkin/gen/Endpoint.java |  578 -----
 .../main/java/com/twitter/zipkin/gen/LogEntry.java |  486 -----
 .../java/com/twitter/zipkin/gen/ResultCode.java    |   45 -
 .../main/java/com/twitter/zipkin/gen/Scribe.java   |  957 ---------
 .../src/main/java/com/twitter/zipkin/gen/Span.java | 1079 ----------
 .../twitter/zipkin/gen/zipkinCoreConstants.java    |   43 -
 .../htrace/impl/ZipkinSpanReceiver.java            |   19 +-
 .../htrace/zipkin/HTraceToZipkinConverter.java     |   13 +-
 .../htrace/TestHTraceSpanToZipkinSpan.java         |   13 +-
 pom.xml                                            |  190 +-
 src/main/assembly/src.xml                          |   92 +
 README.md => src/main/site/markdown/index.md       |  180 +-
 src/main/site/site.xml                             |   70 +
 111 files changed, 9271 insertions(+), 5429 deletions(-)

diff --git a/.gitignore b/.gitignore
index b8acafe..ac643a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,9 @@
 *.iml
 *.orig
 *~
+*.swp
+dependency-reduced-pom.xml
+htrace-core/src/go/src/org/apache/htrace/resource
+!htrace-core/src/go/src/org/apache/htrace/resource/catalog.go
+htrace-core/src/go/build
+htrace-core/src/go/pkg
diff --git a/BUILDING.txt b/BUILDING.txt
new file mode 100644
index 0000000..11213b8
--- /dev/null
+++ b/BUILDING.txt
@@ -0,0 +1,18 @@
+On Building HTrace
+
+Requires go version 1.3.1 or 1.4. Also requires godep. See hbase-core/src/go/BUILDING.txt for more.
+Requires Java 1.7 at least.
+Requires maven 3.x.
+
+After installing go, to build, run:
+
+ $ mvn install
+
+To build a tarball, do:
+
+ $ mvn clean install -DskipTests assembly:single
+
+This will build a tarball into ./target.
+
+To skip the rat-check -- it can take a while -- pass
+a -Drat.skip on the  mvn command-line.
diff --git a/DISCLAIMER.txt b/DISCLAIMER.txt
new file mode 100644
index 0000000..d8c920d
--- /dev/null
+++ b/DISCLAIMER.txt
@@ -0,0 +1,7 @@
+Apache HTrace is an effort undergoing incubation at The Apache Software Foundation (ASF),
+sponsored by the Apache Incubator project. Incubation is required of all newly accepted
+projects until a further review indicates that the infrastructure, communications, and
+decision making process have stabilized in a manner consistent with other successful
+ASF projects. While incubation status is not necessarily a reflection of the
+completeness or stability of the code, it does indicate that the project has yet to be
+fully endorsed by the ASF.
diff --git a/LICENSE.txt b/LICENSE.txt
index d645695..6343a3c 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,3 +1,5 @@
+Apache HTrace (incubating) is Apache 2.0 Licensed. See below for licensing
+of dependencies that are NOT Apache Licensed.
 
                                  Apache License
                            Version 2.0, January 2004
@@ -200,3 +202,47 @@
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
+
+
+levigo, a go wrapper for leveldb, is Copyright (c) 2012 Jeffrey M Hodges
+and MIT licensed: https://github.com/jmhodges/levigo/blob/master/LICENSE
+
+Kingpin, a go command line and flag parser, is Copyright (c) 2014 Alec Thomas
+and MIT licensed: https://github.com/alecthomas/kingpin/blob/master/COPYING
+
+Units, a string formatting go library, is Copyright (c) 2014 Alec Thomas
+and MIT licensed: https://github.com/alecthomas/units/blob/master/COPYING
+
+D3, a javascript library for manipulating data, used by htrace-hbase
+is Copyright 2010-2014, Michael Bostock and BSD licensed:
+https://github.com/mbostock/d3/blob/master/LICENSE
+
+Bootstrap, an html, css, and javascript framework, is
+Copyright (c) 2011-2015 Twitter, Inc and MIT licensed:
+https://github.com/twbs/bootstrap/blob/master/LICENSE
+
+Gorilla mux gorilla/mux implements a request router and dispatcher is BSD
+licensed: https://github.com/gorilla/mux/blob/master/LICENSE
+
+godep, a build dependency is Copyright © 2013 Keith Rarick
+and portions Copyright (c) 2012 The Go Authors. Its a BSD
+license: https://github.com/tools/godep/blob/master/License
+
+
+underscore, a javascript library of functional programming helpers, is
+(c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters
+& Editors and an MIT license:
+https://github.com/jashkenas/underscore/blob/master/LICENSE
+
+jquery, a javascript library, is Copyright jQuery Foundation and other
+contributors, https://jquery.org/. The software consists of
+voluntary contributions made by many individuals. For exact
+contribution history, see the revision history
+available at https://github.com/jquery/jquery
+It is MIT licensed:
+https://github.com/jquery/jquery/blob/master/LICENSE.txt
+
+backbone, is a javascript library, that is Copyright (c) 2010-2014
+Jeremy Ashkenas, DocumentCloud. It is MIT licensed:
+https://github.com/jashkenas/backbone/blob/master/LICENSE
+
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..551cc43
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,12 @@
+This product includes software developed by The Apache Software
+Foundation (http://www.apache.org/).
+
+In addition, this product includes software dependencies. See
+the accompanying LICENSE.txt for a listing of dependencies
+that are NOT Apache licensed (with pointers to their licensing)
+
+Apache HTrace includes an Apache Thrift connector to Zipkin. Zipkin
+is a distributed tracing system that is Apache 2.0 Licensed.
+Copyright 2012 Twitter, Inc.
+
+
diff --git a/README.md b/README.md
index 911a6ab..b3b486e 100644
--- a/README.md
+++ b/README.md
@@ -1,212 +1,22 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
 HTrace
 ======
-HTrace is a tracing framework intended for use with distributed systems written in java.  
-
-The project is hosted at http://github.com/cloudera/htrace.  
-The project is available in Maven Central with groupId: org.htrace, and name: htrace.  
-(It was formally at groupId: org.cloudera.htrace, and name: htrace).  
-
-API
----
-Using HTrace requires some instrumentation to your application.  
-Before we get into that we have to review our terminology.  HTrace
-borrows [Dapper's](http://research.google.com/pubs/pub36356.html)
-terminology.  
-  
-<b>Span:</b> The basic unit of work. For example, sending an RPC is a
-new span, as is sending a response to an RPC.  
-Span's are identified by a unique 64-bit ID for the span and another
-64-bit ID for the trace the span is a part of.  Spans also have other
-data, such as descriptions, key-value annotations, the ID of the span
-that caused them, and process ID's (normally IP address).  
-<br>
-Spans are started and stopped, and they keep track of their timing
-information.  Once you create a span, you must stop it at some point
-in the future.  
-  
-<b>Trace:</b> A set of spans forming a tree-like structure.  For
-example, if you are running a distributed big-data store, a trace
-might be formed by a put request. 
-
-To instrument your system you must:  
-<br>
-<b>1. Attach additional information to your RPC's.</b>  
-In order to create the causal links necessary for a trace, HTrace
-needs to know about the causal
-relationships between spans.  The only information you need to add to
-your RPC's is two 64-bit longs.  If tracing is enabled (Trace.isTracing()
-returns true) when you send an RPC, attach the ID of the current span
-and the ID of the current trace to the message.  
-On the receiving end of the RPC, check to see if the message has the
-additional tracing information above.  If it does, start a new span
-with the information given (more on that in a bit).  
-<br>
-<b>2. Wrap your thread changes.</b>  
-HTrace stores span information in java's ThreadLocals, which causes
-the trace to be "lost" on thread changes. The only way to prevent
-this is to "wrap" your thread changes. For example, if your code looks
-like this:
-
-````java
-    Thread t1 = new Thread(new MyRunnable());
-    ...  
-````
-
-Just change it to look this:  
-
-````java
-    Thread t1 = new Thread(Trace.wrap(new MyRunnable()));
-````
-
-That's it! `Trace.wrap()` takes a single argument (a runnable or a
-callable) and if the current thread is a part of a trace, returns a
-wrapped version of the argument.  The wrapped version of a callable
-and runnable just knows about the span that created it and will start
-a new span in the new thread that is the child of the span that
-created the runnable/callable.  There may be situations in which a
-simple `Trace.wrap()` does not suffice.  In these cases all you need
-to do is keep a reference to the "parent span" (the span before the
-thread change) and once you're in the new thread start a new span that
-is the "child" of the parent span you stored.  
-<br>
-For example:  
-<br>
-Say you have some object representing a "put" operation.  When the
-client does a "put," the put is first added to a list so another
-thread can batch together the puts. In this situation, you
-might want to add another field to the Put class that could store the
-current span at the time the put was created.  Then when the put is
-pulled out of the list to be processed, you can start a new span as
-the child of the span stored in the Put.  
-<br>
-<b>3. Add custom spans and annotations.</b>  
-Once you've augmented your RPC's and wrapped the necessary thread
-changes, you can add more spans and annotations wherever you want.  
-For example, you might do some expensive computation that you want to
-see on your traces.  In this case, you could start a new span before
-the computation that you then stop after the computation has
-finished. It might look like this:  
-
-````java
-    Span computationSpan = Trace.startSpan("Expensive computation.");  
-    try {  
-        //expensive computation here  
-    } finally {  
-        computationSpan.stop();  
-    }  
-````
-
-HTrace also supports key-value annotations on a per-trace basis.  
-<br>
-Example:
-
-````java
-    Trace.currentTrace().addAnnotation("faultyRecordCounter".getBytes(), "1".getBytes());
-````
-
-`Trace.currentTrace()` will not return `null` if the current thread is
-not tracing, but instead it will return a `NullSpan`, which does
-nothing on any of its method calls. The takeaway here is you can call
-methods on the `currentTrace()` without fear of NullPointerExceptions.
-
-###Samplers  
-`Sampler` is an interface that defines one function:  
-
-````java
-    boolean next(T info);
-````
-
-All of the `Trace.startSpan()` methods can take an optional sampler.  
-A new span is only created if the sampler's next function returns
-true.  If the Sampler returns false, the `NullSpan` is returned from
-`startSpan()`, so it's safe to call `stop()` or `addAnnotation()` on it.
-As you may have noticed from the `next()` method signature, Sampler is
-parameterized.  The argument to `next()` is whatever piece of
-information you might need for sampling.  See `Sampler.java` for an
-example of this.  If you do not require any additional information,
-then just ignore the parameter.  
-HTrace includes  a sampler that always returns true, a
-sampler that always returns false and a sampler returns true some
-percentage of the time (you pass in the percentage as a decimal at construction). 
-
-###`Trace.startSpan()` 
-There is a single method to create and start spans: `startSpan()`.  
-For the `startSpan()` methods that do not take an explicit Sampler, the
-default Sampler is used.  The default sampler returns true if and only
-if tracing is already on in the current thread.  That means that
-calling `startSpan()` with no explicit Sampler is a good idea when you
-have information that you would like to add to a trace if it's already
-occurring, but is not something you would want to start a whole new
-trace for.  
-<br>
-If you are using a sampler that makes use of the `T info` parameter to
-`next()`, just pass in the object as the last argument.  If you leave it
-out, HTrace will pass `null` for you (so make sure your Samplers can
-handle `null`).  
-<br>
-Aside from whether or not you pass in an explicit `Sampler`, there are
-other options you have when calling `startSpan()`.  
-For the next section I am assuming you are familiar with the options
-for passing in `Samplers` and `info` parameters, so when I say "no
-arguments," I mean no additional arguments other than whatever
-`Sampler`/`info` parameters you deem necessary.  
-<br>
-You can call `startSpan()` with no additional arguments.
-In this case, `Trace.java` will start a span if the sampler (explicit
-or default) returns true. If the current span is not the `NullSpan`, the span
-returned will be a child of the current span, otherwise it will start
-a new trace in the current thread (it will be a
-`ProcessRootMilliSpan`). All of the other `startSpan()` methods take some
-parameter describing the parent span of the span to be created. The
-versions that take a `TraceInfo` or a `long traceId` and `long
-parentId` will mostly be used when continuing a trace over RPC. The
-receiver of the RPC will check the message for the additional two
-`longs` and will call `startSpan()` if they are attached.  The last
-`startSpan()` takes a `Span parent`.  The result of `parent.child()`
-will be used for the new span.  `Span.child()` simply returns a span
-that is a child of `this`. 
-
-###Span Receivers
-In order to use the tracing information consisting of spans,
-you need implementation of `SpanReceiver` interface which collects spans
-and typically writes it to files or databases or collector services.
-The `SpanReceiver` implementation must provide `receiveSpan` method which
-is called from `Trace.deliver` method.
-You do not need to explicitly call `Trace.deliver`
-because it is internally called by the implementation of `Span`.
-
-````java
-    public interface SpanReceiver extends Closeable {
-      public void configure(HTraceConfiguration conf);
-      public void receiveSpan(Span span);
-    }
-````
-
-Each application process using HTrace needs to
-initialize the SpanReceiver implementation and register it first
-by calling `Trace.addReceiver` method.
-
-````java
-    // load and instanciate SpanReceiver impl.
-    // ...
-    impl.configure(conf);
-    Trace.addReceiver(impl);
-````
-
-htrace-zipkin provides the `SpanReceiver` implementation
-which sends spans to [Zipkin](https://github.com/twitter/zipkin) collector.
-You can build the uber-jar (htrace-zipkin-*-jar-withdependency.jar) for manual setup as shown below.
-This ubse-jar contains depencdencies except htrace-core and its dependencies.
-
-    $ cd htrace-zipkin
-    $ mvn compile assembly:single
-
-Testing Information
--------------------------------
-
-The test that creates a sample trace (TestHTrace) takes a command line argument telling it where to write span information. Run mvn test -DargLine="-DspanFile=FILE\_PATH" to write span information to FILE_PATH. If no file is specified, span information will be written to standard out. If span information is written to a file, you can use the included graphDrawer python script in tools/ to create a simple visualization of the trace. Or you could write some javascript to make a better visu [...]
+HTrace is a tracing framework for use with distributed systems written in java.
 
-Publishing to Maven Central
--------------------------------
-See [OSSRH-8896](https://issues.sonatype.org/browse/OSSRH-8896)
-for repository vitals.
+See documentation at src/main/site/markdown/index.md or at http://htrace.incubator.apache.org.
diff --git a/htrace-core/pom.xml b/htrace-core/pom.xml
index b6911d1..2133cb0 100644
--- a/htrace-core/pom.xml
+++ b/htrace-core/pom.xml
@@ -17,12 +17,13 @@ language governing permissions and limitations under the License. -->
 
   <parent>
     <artifactId>htrace</artifactId>
-    <groupId>org.htrace</groupId>
-    <version>3.0.4</version>
+    <groupId>org.apache.htrace</groupId>
+    <version>3.1.0-incubating</version>
+    <relativePath>..</relativePath>
   </parent>
 
   <name>htrace-core</name>
-  <url>https://github.com/cloudera/htrace</url>
+  <url>http://incubator.apache.org/projects/htrace.html</url>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -31,6 +32,13 @@ language governing permissions and limitations under the License. -->
   <build>
     <plugins>
       <plugin>
+        <!--Make it so assembly:single does nothing in here-->
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <skipAssembly>true</skipAssembly>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
       </plugin>
@@ -42,9 +50,84 @@ language governing permissions and limitations under the License. -->
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <configuration>
+              <relocations>
+                <relocation>
+                  <pattern>org.apache.commons.logging</pattern>
+                  <shadedPattern>org.apache.htrace.commons.logging</shadedPattern>
+                </relocation>
+                <relocation>
+                  <pattern>com.fasterxml.jackson</pattern>
+                  <shadedPattern>org.apache.htrace.fasterxml.jackson</shadedPattern>
+                </relocation>
+              </relocations>
+            </configuration>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <id>go_compile</id>
+            <goals><goal>run</goal></goals>
+            <configuration>
+              <tasks>
+                <exec executable="./gobuild.sh" 
+                      dir="${basedir}/src/go/"
+                      failonerror="true"> 
+                  <env key="RELEASE_VERSION" value="${version}"/>
+                </exec>
+              </tasks>
+            </configuration>
+          </execution>
+          <execution>
+            <phase>test</phase>
+            <id>go_test</id>
+            <goals><goal>run</goal></goals>
+            <configuration>
+              <tasks>
+                <exec executable="bash" failonerror="true" dir="${basedir}/src/go">
+                  <arg value="-c"/>
+                  <arg value="[ x$SKIPTESTS = xtrue ] || ./gobuild.sh test"/>
+                  <env key="SKIPTESTS" value="${skipTests}"/>
+                </exec>
+              </tasks>
+            </configuration>
+          </execution>
+          <execution>
+            <phase>clean</phase>
+            <id>go_clean</id>
+            <goals><goal>run</goal></goals>
+            <configuration>
+              <tasks>
+                <exec executable="bash" failonerror="true" dir="${basedir}/src/go">
+                  <arg value="-c"/>
+                  <arg value="./gobuild.sh clean"/>
+                </exec>
+              </tasks>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-gpg-plugin</artifactId>
       </plugin>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
         <groupId>org.apache.rat</groupId>
         <artifactId>apache-rat-plugin</artifactId>
       </plugin>
@@ -58,23 +141,24 @@ language governing permissions and limitations under the License. -->
   <dependencies>
     <!-- Global deps. -->
     <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
     </dependency>
+    <!-- core specific deps. -->
     <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
     </dependency>
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>2.4.0</version>
     </dependency>
-    <!-- core specific deps. -->
     <dependency>
-      <groupId>org.mortbay.jetty</groupId>
-      <artifactId>jetty-util</artifactId>
-      <version>6.1.26</version>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>2.4.0</version>
     </dependency>
   </dependencies>
 </project>
diff --git a/htrace-core/src/go/BUILDING.txt b/htrace-core/src/go/BUILDING.txt
new file mode 100644
index 0000000..bb3eb88
--- /dev/null
+++ b/htrace-core/src/go/BUILDING.txt
@@ -0,0 +1,31 @@
+Building the HTrace Go code
+===========================
+The htrace go code consists of 4 main parts:
+* The "htraced" standalone server
+    This is a server which accepts trace spans, and services REST queries.
+
+* The "htrace" command-line program which can query the server
+    This is a simple command-line program which can query the htrace server.
+
+* The htraced Javascript Web UI (not yet implemented)
+
+* The htrace go client library (not yet implemented)
+    This is the equivalent of the Java HTrace client library, but written in Go.
+
+You can build all these parts simply by running "gobuild.sh".
+The binaries will be created in bin/.
+
+Dependencies
+============
+You will need to install:
+* The Go programming language
+* The development package for leveldb (some Linux distros call this "leveldb-devel") containing libleveldb.so
+* Version 0 of the "godep" tool from https://github.com/tools/godep/releases
+
+htraced requires libleveldb.so to be in your shared library path in order to run.
+You can set LD_LIBRARY_PATH to the path for this library, or simply install
+libleveldb.so to your system library path.
+
+Testing
+=======
+You can run the unit tests by running "./gobuild.sh test"
diff --git a/htrace-core/src/go/Godeps/Godeps.json b/htrace-core/src/go/Godeps/Godeps.json
new file mode 100644
index 0000000..10c8e5d
--- /dev/null
+++ b/htrace-core/src/go/Godeps/Godeps.json
@@ -0,0 +1,26 @@
+{
+    "ImportPath": "git-wip-us.apache.org/repos/asf/incubator-htrace.git",
+    "GoVersion": "go1.3.1",
+    "Deps": [
+        {
+            "ImportPath": "github.com/alecthomas/kingpin",
+            "Rev": "afafa8aab106d31c9dc8f5e562b3f30f6246c3d4"
+        },
+        {
+            "ImportPath": "github.com/alecthomas/units",
+            "Rev": "6b4e7dc5e3143b85ea77909c72caf89416fc2915"
+        },
+        {
+            "ImportPath": "github.com/gorilla/context",
+            "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
+        },
+        {
+            "ImportPath": "github.com/gorilla/mux",
+            "Rev": "e444e69cbd2e2e3e0749a2f3c717cec491552bbf"
+        },
+        {
+            "ImportPath": "github.com/jmhodges/levigo",
+            "Rev": "2c43dde93d0e056173706534afd514fcbc1dd578"
+        }
+    ]
+}
diff --git a/htrace-core/src/go/format.sh b/htrace-core/src/go/format.sh
new file mode 100755
index 0000000..6b1c8ae
--- /dev/null
+++ b/htrace-core/src/go/format.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# Reformats the HTrace code.
+#
+# ./format.sh               Reformats all code.
+#
+
+die() {
+    echo $@
+    exit 1
+}
+
+# Check for gofmt.  It should be installed whenever the go developement tools
+# are installed.
+which gofmt &> /dev/null
+[ $? -ne 0 ] && die "You must install the gofmt code reformatting formatting tool."
+
+# Find go sources.  We assume no newlines or whitespace in file names.
+SCRIPT_DIR="$(cd "$( dirname $0 )" && pwd)"
+find "${SCRIPT_DIR}" -noleaf -xdev -name '*.go' | xargs -l gofmt -w
diff --git a/htrace-core/src/go/gobuild.sh b/htrace-core/src/go/gobuild.sh
new file mode 100755
index 0000000..4aad01b
--- /dev/null
+++ b/htrace-core/src/go/gobuild.sh
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# Builds the HTrace server code.
+#
+# ./build.sh                Builds the code.
+# ./build.sh test           Builds and runs all unit tests.
+# ./build.sh bench          Builds and runs all benchmarks
+#
+
+die() {
+    echo $@
+    exit 1
+}
+
+ACTION=install
+if [ $# -gt 0 ]; then
+    ACTION="${1}"
+    shift
+fi
+RELEASE_VERSION=${RELEASE_VERSION:-unknown}
+
+# Set up directories.  The build/ directory is where build dependencies and
+# build binaries should go.
+SCRIPT_DIR="$(cd "$( dirname $0 )" && pwd)"
+export GOBIN="${SCRIPT_DIR}/build"
+mkdir -p "${GOBIN}" || die "failed to mkdir -p ${GOBIN}"
+cd "${GOBIN}" || die "failed to cd to ${SCRIPT_DIR}"
+export GOPATH="${GOBIN}:${SCRIPT_DIR}"
+
+# Check for go
+which go &> /dev/null
+if [ $? -ne 0 ]; then
+    cat <<EOF
+You must install the Golang programming language.
+
+If you are using Debian, try "apt-get install golang".
+For Red Hat, try "yum install go".
+For other distributions and operating systems use your packaging tool.
+EOF
+exit 1
+fi
+
+# Check for libleveldb.so
+if [ -x "/sbin/ldconfig" ]; then
+    # Suse requires ldconfig to be run via the absolute path
+    ldconfig=/sbin/ldconfig
+else
+    which ldconfig &> /dev/null
+    [ $? -eq 0 ] && ldconfig=ldconfig
+fi
+if [ -n "${ldconfig}" ]; then
+    if "${ldconfig}" -p | grep -q libleveldb; then
+        :
+    else
+        echo "You must install the leveldb-devel package (or distro-specific equivalent.)"
+        exit 1
+    fi
+fi
+
+case $ACTION in
+clean)
+    rm -rf -- "${GOBIN}" ${SCRIPT_DIR}/pkg
+    find "${SCRIPT_DIR}/src/org/apache/htrace/resource" ! -name 'catalog.go' \
+        -type f -exec rm -f {} +
+    ;;
+install)
+    # Ensure that we have the godep program.
+    PATH="${PATH}:${GOBIN}"
+    which godep &> /dev/null
+    if [ $? -ne 0 ]; then
+        echo "Installing godep..."
+        go get github.com/tools/godep || die "failed to get godep"
+    fi
+
+    # Download dependencies into the build directory.
+    echo "godep restore..."
+    godep restore || die "failed to set up dependencies"
+    go run "$SCRIPT_DIR/src/org/apache/htrace/bundler/bundler.go" \
+        --src="$SCRIPT_DIR/../web/" --dst="$SCRIPT_DIR/src/org/apache/htrace/resource/" \
+            || die "bundler failed"
+
+    # Discover the git version
+    GIT_VERSION=$(git rev-parse HEAD)
+    [ $? -eq 0 ] || GIT_VERSION="unknown"
+
+    # Inject the release and git version into the htraced ldflags.
+    FLAGS="-X main.RELEASE_VERSION ${RELEASE_VERSION} -X main.GIT_VERSION ${GIT_VERSION}"
+    go install -ldflags "${FLAGS}" -v org/apache/htrace/... "$@"
+    ;;
+bench)
+    go test -v org/apache/htrace/... -test.bench=. "$@"
+    ;;
+*)
+    go ${ACTION} -v org/apache/htrace/... "$@"
+    ;;
+esac
diff --git a/htrace-core/src/go/src/org/apache/htrace/bundler/bundler.go b/htrace-core/src/go/src/org/apache/htrace/bundler/bundler.go
new file mode 100644
index 0000000..982dc8f
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/bundler/bundler.go
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+//
+// The bundler turns files into resources contained in go code.
+//
+// This is useful for serving HTML and Javascript files from a self-contained binary.
+//
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+const APACHE_HEADER = `/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+`
+
+const GENERATED_CODE_COMMENT = "// THIS IS GENERATED CODE.  DO NOT EDIT."
+
+var SEP string = string(os.PathSeparator)
+
+// Return true if a file contains a given string.
+func fileContainsString(path, line string) (bool, error) {
+	file, err := os.Open(path)
+	if err != nil {
+		return false, err
+	}
+	defer file.Close()
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		if strings.Contains(scanner.Text(), line) {
+			return true, nil
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return false, err
+	}
+	return false, nil
+}
+
+// Converts a source file path to a destination file path
+func sfileToDfile(sfile string) string {
+	return strings.Replace(sfile, SEP, "__", -1) + ".go"
+}
+
+// Delete generated files that are in dfiles but not sfiles.
+// sfiles and dfiles must be sorted by file name.
+func deleteUnusedDst(sfiles []string, dst string, dfiles []os.FileInfo) error {
+	s := 0
+	for d := range dfiles {
+		fullDst := dst + SEP + dfiles[d].Name()
+		generated, err := fileContainsString(fullDst, GENERATED_CODE_COMMENT)
+		if err != nil {
+			return err
+		}
+		if !generated {
+			// Skip this destination file, since it is not generated.
+			continue
+		}
+		found := false
+		for {
+			if s >= len(sfiles) {
+				break
+			}
+			tgt := sfileToDfile(sfiles[s])
+			if tgt == dfiles[d].Name() {
+				found = true
+				break
+			}
+			if tgt > dfiles[d].Name() {
+				break
+			}
+			s++
+		}
+		if !found {
+			log.Printf("Removing %s\n", fullDst)
+			err := os.Remove(fullDst)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func createBundleFile(pkg, src, sfile, dst string) error {
+	// Open destination file and write header.
+	tgt := sfileToDfile(sfile)
+	fullDst := dst + SEP + tgt
+	out, err := os.Create(fullDst)
+	if err != nil {
+		return err
+	}
+	defer out.Close()
+	_, err = out.WriteString(APACHE_HEADER)
+	if err != nil {
+		return err
+	}
+	_, err = out.WriteString("\n" + GENERATED_CODE_COMMENT + "\n")
+	if err != nil {
+		return err
+	}
+	_, err = out.WriteString(fmt.Sprintf("\npackage %s\n", pkg))
+	if err != nil {
+		return err
+	}
+	_, err = out.WriteString(fmt.Sprintf("var _ = addResource(\"%s\", `\n", tgt[:len(tgt)-3]))
+	if err != nil {
+		return err
+	}
+
+	// Open source file and create scanner.
+	fullSrc := src + SEP + sfile
+	in, err := os.Open(fullSrc)
+	if err != nil {
+		return err
+	}
+	defer in.Close()
+	reader := bufio.NewReader(in)
+	scanner := bufio.NewScanner(reader)
+	for scanner.Scan() {
+		_, err := out.WriteString(strings.Replace(scanner.Text(), "`", "` + \"`\" + `", -1) + "\n")
+		if err != nil {
+			return err
+		}
+	}
+	_, err = out.WriteString("`)\n")
+	if err != nil {
+		return err
+	}
+	err = out.Close()
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+var src = flag.String("src", "", "Source path for bundled resources.")
+var dst = flag.String("dst", "", "Destination path for bundled resources.")
+var pkg = flag.String("pkg", "resource", "Package name to use for bundled resources")
+
+func main() {
+	flag.Parse()
+	if *src == "" {
+		log.Fatal("You must supply a src directory to bundle.")
+	}
+	if *dst == "" {
+		log.Fatal("You must supply a dst directory for output.")
+	}
+	sfiles := make([]string, 0, 32)
+	absSrc, err := filepath.Abs(filepath.Clean(*src))
+	if err != nil {
+		log.Fatalf("Error getting absolute path for %s: %s\n", *src, err.Error())
+	}
+	err = filepath.Walk(absSrc, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		if info.IsDir() {
+			return nil
+		}
+		var suffix string
+		suffix, err = filepath.Rel(absSrc, path)
+		if err != nil {
+			return err
+		}
+		sfiles = append(sfiles, suffix)
+		return nil
+	})
+	if err != nil {
+		log.Fatalf("Error listing files in src directory %s: %s\n", absSrc, err.Error())
+	}
+	var dfiles []os.FileInfo
+	dfiles, err = ioutil.ReadDir(*dst)
+	if err != nil {
+		log.Fatalf("Error listing files in output directory %s: %s\n", *dst, err.Error())
+	}
+	deleteUnusedDst(sfiles, *dst, dfiles)
+	for s := range sfiles {
+		err = createBundleFile(*pkg, absSrc, sfiles[s], *dst)
+		if err != nil {
+			log.Fatalf("Error creating bundle file for %s in %s: %s\n",
+				sfiles[s], *dst, err.Error())
+		}
+		log.Printf("Bundled %s as %s\n", absSrc, absSrc+SEP+sfileToDfile(sfiles[s]))
+	}
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/rest.go b/htrace-core/src/go/src/org/apache/htrace/common/rest.go
new file mode 100644
index 0000000..eeb9568
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/common/rest.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+// Info returned by /serverInfo
+type ServerInfo struct {
+	// The server release version.
+	ReleaseVersion string
+
+	// The git hash that this software was built with.
+	GitVersion string
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/span.go b/htrace-core/src/go/src/org/apache/htrace/common/span.go
new file mode 100644
index 0000000..540ba12
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/common/span.go
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+)
+
+//
+// Represents a trace span.
+//
+// Compatibility notes:
+// When converting to JSON, we store the 64-bit numbers as hexadecimal strings rather than as
+// integers.  This is because JavaScript lacks the ability to handle 64-bit integers.  Numbers above
+// about 55 bits will be rounded by Javascript.  Since the Javascript UI is a primary consumer of
+// this JSON data, we have to simply pass it as a string.
+//
+
+type TraceInfoMap map[string][]byte
+
+type TimelineAnnotation struct {
+	Time int64  `json:"t"`
+	Msg  string `json:"m"`
+}
+
+type SpanId int64
+
+func (id SpanId) String() string {
+	return fmt.Sprintf("%016x", id)
+}
+
+func (id SpanId) Val() int64 {
+	return int64(id)
+}
+
+func (id SpanId) MarshalJSON() ([]byte, error) {
+	return []byte(`"` + fmt.Sprintf("%016x", uint64(id)) + `"`), nil
+}
+
+func (id *SpanId) UnMarshalJSON(b []byte) error {
+	v, err := strconv.ParseUint(string(b), 16, 64)
+	if err != nil {
+		return err
+	}
+	*id = SpanId(v)
+	return nil
+}
+
+type SpanData struct {
+	Begin               int64                `json:"b"`
+	End                 int64                `json:"e"`
+	Description         string               `json:"d"`
+	TraceId             SpanId               `json:"i"`
+	Parents             []SpanId             `json:"p"`
+	Info                TraceInfoMap         `json:"n,omitempty"`
+	ProcessId           string               `json:"r"`
+	TimelineAnnotations []TimelineAnnotation `json:"t,omitempty"`
+}
+
+type Span struct {
+	Id SpanId `json:"s,string"`
+	SpanData
+}
+
+func (span *Span) ToJson() []byte {
+	jbytes, err := json.Marshal(*span)
+	if err != nil {
+		panic(err)
+	}
+	return jbytes
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/span_test.go b/htrace-core/src/go/src/org/apache/htrace/common/span_test.go
new file mode 100644
index 0000000..f218b3a
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/common/span_test.go
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"testing"
+)
+
+func TestSpanToJson(t *testing.T) {
+	t.Parallel()
+	span := Span{Id: 2305843009213693952,
+		SpanData: SpanData{
+			Begin:       123,
+			End:         456,
+			Description: "getFileDescriptors",
+			TraceId:     999,
+			Parents:     []SpanId{},
+			ProcessId:   "testProcessId",
+		}}
+	ExpectStrEqual(t,
+		`{"s":"2000000000000000","b":123,"e":456,"d":"getFileDescriptors","i":"00000000000003e7","p":[],"r":"testProcessId"}`,
+		string(span.ToJson()))
+}
+
+func TestAnnotatedSpanToJson(t *testing.T) {
+	t.Parallel()
+	span := Span{Id: 1305813009213693952,
+		SpanData: SpanData{
+			Begin:       1234,
+			End:         4567,
+			Description: "getFileDescriptors2",
+			TraceId:     999,
+			Parents:     []SpanId{},
+			ProcessId:   "testAnnotatedProcessId",
+			TimelineAnnotations: []TimelineAnnotation{
+				TimelineAnnotation{
+					Time: 7777,
+					Msg:  "contactedServer",
+				},
+				TimelineAnnotation{
+					Time: 8888,
+					Msg:  "passedFd",
+				},
+			},
+		}}
+	ExpectStrEqual(t,
+		`{"s":"121f2e036d442000","b":1234,"e":4567,"d":"getFileDescriptors2","i":"00000000000003e7","p":[],"r":"testAnnotatedProcessId","t":[{"t":7777,"m":"contactedServer"},{"t":8888,"m":"passedFd"}]}`,
+		string(span.ToJson()))
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/common/test_util.go b/htrace-core/src/go/src/org/apache/htrace/common/test_util.go
new file mode 100644
index 0000000..871c847
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/common/test_util.go
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package common
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+type Int64Slice []int64
+
+func (p Int64Slice) Len() int           { return len(p) }
+func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] }
+func (p Int64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+type SupplierFun func() bool
+
+//
+// Wait for a configurable amount of time for a precondition to become true.
+//
+// Example:
+//   WaitFor(time.Minute * 1, time.Millisecond * 1, func() bool {
+//      return ht.Store.GetStatistics().NumSpansWritten >= 3
+//  })
+//
+func WaitFor(dur time.Duration, poll time.Duration, fun SupplierFun) {
+	if poll == 0 {
+		poll = dur / 10
+	}
+	if poll <= 0 {
+		panic("Can't have a polling time less than zero.")
+	}
+	endTime := time.Now().Add(dur)
+	for {
+		if fun() {
+			return
+		}
+		if !time.Now().Before(endTime) {
+			break
+		}
+		time.Sleep(poll)
+	}
+	panic(fmt.Sprintf("Timed out after %s", dur))
+}
+
+// Trigger a test failure if two strings are not equal.
+func ExpectStrEqual(t *testing.T, expect string, actual string) {
+	if expect != actual {
+		t.Fatalf("Expected:\n%s\nGot:\n%s\n", expect, actual)
+	}
+}
+
+// Trigger a test failure if the JSON representation of two spans are not equals.
+func ExpectSpansEqual(t *testing.T, spanA *Span, spanB *Span) {
+	ExpectStrEqual(t, string(spanA.ToJson()), string(spanB.ToJson()))
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/conf/config.go b/htrace-core/src/go/src/org/apache/htrace/conf/config.go
new file mode 100644
index 0000000..528d6c1
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/conf/config.go
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package conf
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"strconv"
+	"strings"
+	"syscall"
+)
+
+//
+// The configuration code for HTraced.
+//
+// HTraced can be configured via Hadoop-style XML configuration files, or by passing -Dkey=value
+// command line arguments.  Command-line arguments without an equals sign, such as "-Dkey", will be
+// treated as setting the key to "true".
+//
+// Configuration key constants should be defined in config_keys.go.  Each key should have a default,
+// which will be used if the user supplies no value, or supplies an invalid value.
+// For that reason, it is not necessary for the Get, GetInt, etc. functions to take a default value
+// argument.
+//
+
+type Config struct {
+	settings map[string]string
+	defaults map[string]string
+}
+
+type Builder struct {
+	// If non-nil, the XML configuration file to read.
+	Reader io.Reader
+
+	// If non-nil, the configuration values to use.
+	Values map[string]string
+
+	// If non-nil, the default configuration values to use.
+	Defaults map[string]string
+
+	// If non-nil, the command-line arguments to use.
+	Argv []string
+}
+
+// Load a configuration from the application's argv, configuration file, and the standard
+// defaults.
+func LoadApplicationConfig() *Config {
+	reader, err := openFile(CONFIG_FILE_NAME, []string{"."})
+	if err != nil {
+		log.Fatal("Error opening config file: " + err.Error())
+	}
+	bld := Builder{}
+	if reader != nil {
+		defer reader.Close()
+		bld.Reader = bufio.NewReader(reader)
+	}
+	bld.Argv = os.Args[1:]
+	bld.Defaults = DEFAULTS
+	var cnf *Config
+	cnf, err = bld.Build()
+	if err != nil {
+		log.Fatal("Error building configuration: " + err.Error())
+	}
+	os.Args = append(os.Args[0:1], bld.Argv...)
+	return cnf
+}
+
+// Attempt to open a configuration file somewhere on the provided list of paths.
+func openFile(cnfName string, paths []string) (io.ReadCloser, error) {
+	for p := range paths {
+		path := fmt.Sprintf("%s%c%s", paths[p], os.PathSeparator, cnfName)
+		file, err := os.Open(path)
+		if err == nil {
+			log.Println("Reading configuration from " + path)
+			return file, nil
+		}
+		if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOENT {
+			continue
+		}
+		log.Println("Error opening " + path + " for read: " + err.Error())
+	}
+	return nil, nil
+}
+
+// Build a new configuration object from the provided conf.Builder.
+func (bld *Builder) Build() (*Config, error) {
+	// Load values and defaults
+	cnf := Config{}
+	cnf.settings = make(map[string]string)
+	if bld.Values != nil {
+		for k, v := range bld.Values {
+			cnf.settings[k] = v
+		}
+	}
+	cnf.defaults = make(map[string]string)
+	if bld.Defaults != nil {
+		for k, v := range bld.Defaults {
+			cnf.defaults[k] = v
+		}
+	}
+
+	// Process the configuration file, if we have one
+	if bld.Reader != nil {
+		parseXml(bld.Reader, cnf.settings)
+	}
+
+	// Process command line arguments
+	var i int
+	for i < len(bld.Argv) {
+		str := bld.Argv[i]
+		if strings.HasPrefix(str, "-D") {
+			idx := strings.Index(str, "=")
+			if idx == -1 {
+				key := str[2:]
+				cnf.settings[key] = "true"
+			} else {
+				key := str[2:idx]
+				val := str[idx+1:]
+				cnf.settings[key] = val
+			}
+			bld.Argv = append(bld.Argv[:i], bld.Argv[i+1:]...)
+		} else {
+			i++
+		}
+	}
+	return &cnf, nil
+}
+
+// Get a string configuration key.
+func (cnf *Config) Get(key string) string {
+	ret := cnf.settings[key]
+	if ret != "" {
+		return ret
+	}
+	return cnf.defaults[key]
+}
+
+// Get a boolean configuration key.
+func (cnf *Config) GetBool(key string) bool {
+	str := cnf.settings[key]
+	ret, err := strconv.ParseBool(str)
+	if err == nil {
+		return ret
+	}
+	str = cnf.defaults[key]
+	ret, err = strconv.ParseBool(str)
+	if err == nil {
+		return ret
+	}
+	return false
+}
+
+// Get an integer configuration key.
+func (cnf *Config) GetInt(key string) int {
+	str := cnf.settings[key]
+	ret, err := strconv.Atoi(str)
+	if err == nil {
+		return ret
+	}
+	str = cnf.defaults[key]
+	ret, err = strconv.Atoi(str)
+	if err == nil {
+		return ret
+	}
+	return 0
+}
+
+// Get an int64 configuration key.
+func (cnf *Config) GetInt64(key string) int64 {
+	str := cnf.settings[key]
+	ret, err := strconv.ParseInt(str, 10, 64)
+	if err == nil {
+		return ret
+	}
+	str = cnf.defaults[key]
+	ret, err = strconv.ParseInt(str, 10, 64)
+	if err == nil {
+		return ret
+	}
+	return 0
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/conf/config_keys.go b/htrace-core/src/go/src/org/apache/htrace/conf/config_keys.go
new file mode 100644
index 0000000..b4e5994
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/conf/config_keys.go
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package conf
+
+import (
+	"fmt"
+	"os"
+)
+
+//
+// Configuration keys for HTrace.
+//
+
+// The platform-specific path separator.  Usually slash.
+var PATH_SEP string = fmt.Sprintf("%c", os.PathSeparator)
+
+// The platform-specific path list separator.  Usually colon.
+var PATH_LIST_SEP string = fmt.Sprintf("%c", os.PathListSeparator)
+
+// The name of the XML configuration file to look for.
+const CONFIG_FILE_NAME = "htraced.xml"
+
+// The web address to start the REST server on.
+const HTRACE_WEB_ADDRESS = "web.address"
+
+// The default port for the Htrace web address.
+const HTRACE_WEB_ADDRESS_DEFAULT_PORT = 9095
+
+// The directories to put the data store into.  Separated by PATH_LIST_SEP.
+const HTRACE_DATA_STORE_DIRECTORIES = "data.store.directories"
+
+// Boolean key which indicates whether we should clear data on startup.
+const HTRACE_DATA_STORE_CLEAR = "data.store.clear"
+
+// How many writes to buffer before applying backpressure to span senders.
+const HTRACE_DATA_STORE_SPAN_BUFFER_SIZE = "data.store.span.buffer.size"
+
+// Default values for HTrace configuration keys.
+var DEFAULTS = map[string]string{
+	HTRACE_WEB_ADDRESS: fmt.Sprintf("0.0.0.0:%d", HTRACE_WEB_ADDRESS_DEFAULT_PORT),
+	HTRACE_DATA_STORE_DIRECTORIES: PATH_SEP + "tmp" + PATH_SEP + "htrace1" +
+		PATH_LIST_SEP + PATH_SEP + "tmp" + PATH_SEP + "htrace2",
+	HTRACE_DATA_STORE_CLEAR:            "false",
+	HTRACE_DATA_STORE_SPAN_BUFFER_SIZE: "100",
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/conf/config_test.go b/htrace-core/src/go/src/org/apache/htrace/conf/config_test.go
new file mode 100644
index 0000000..b16abb4
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/conf/config_test.go
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package conf
+
+import (
+	"strings"
+	"testing"
+)
+
+// Test that parsing command-line arguments of the form -Dfoo=bar works.
+func TestParseArgV(t *testing.T) {
+	t.Parallel()
+	argv := []string{"-Dfoo=bar", "-Dbaz=123", "-DsillyMode"}
+	bld := &Builder{Argv: argv}
+	cnf, err := bld.Build()
+	if err != nil {
+		t.Fatal()
+	}
+	if "bar" != cnf.Get("foo") {
+		t.Fatal()
+	}
+	if 123 != cnf.GetInt("baz") {
+		t.Fatal()
+	}
+	if !cnf.GetBool("sillyMode") {
+		t.Fatal()
+	}
+	if cnf.GetBool("otherSillyMode") {
+		t.Fatal()
+	}
+}
+
+// Test that default values work.
+// Defaults are used only when the configuration option is not present or can't be parsed.
+func TestDefaults(t *testing.T) {
+	t.Parallel()
+	argv := []string{"-Dfoo=bar", "-Dbaz=invalidNumber"}
+	defaults := map[string]string{
+		"foo":  "notbar",
+		"baz":  "456",
+		"foo2": "4611686018427387904",
+	}
+	bld := &Builder{Argv: argv, Defaults: defaults}
+	cnf, err := bld.Build()
+	if err != nil {
+		t.Fatal()
+	}
+	if "bar" != cnf.Get("foo") {
+		t.Fatal()
+	}
+	if 456 != cnf.GetInt("baz") {
+		t.Fatal()
+	}
+	if 4611686018427387904 != cnf.GetInt64("foo2") {
+		t.Fatal()
+	}
+}
+
+// Test that we can parse our XML configuration file.
+func TestXmlConfigurationFile(t *testing.T) {
+	t.Parallel()
+	xml := `
+<?xml version="1.0"?>
+<?xml-stylesheet type=\"text/xsl\" href=\"configuration.xsl\"?>
+<configuration>
+  <property>
+    <name>foo.bar</name>
+    <value>123</value>
+  </property>
+  <property>
+    <name>foo.baz</name>
+    <value>xmlValue</value>
+  </property>
+  <!--<property>
+    <name>commented.out</name>
+    <value>stuff</value>
+  </property>-->
+</configuration>
+`
+	xmlReader := strings.NewReader(xml)
+	argv := []string{"-Dfoo.bar=456"}
+	defaults := map[string]string{
+		"foo.bar":     "789",
+		"cmdline.opt": "4611686018427387904",
+	}
+	bld := &Builder{Argv: argv, Defaults: defaults, Reader: xmlReader}
+	cnf, err := bld.Build()
+	if err != nil {
+		t.Fatal()
+	}
+	// The command-line argument takes precedence over the XML and the defaults.
+	if 456 != cnf.GetInt("foo.bar") {
+		t.Fatal()
+	}
+	if "xmlValue" != cnf.Get("foo.baz") {
+		t.Fatalf("foo.baz = %s", cnf.Get("foo.baz"))
+	}
+	if "" != cnf.Get("commented.out") {
+		t.Fatal()
+	}
+	if 4611686018427387904 != cnf.GetInt64("cmdline.opt") {
+		t.Fatal()
+	}
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/conf/xml.go b/htrace-core/src/go/src/org/apache/htrace/conf/xml.go
new file mode 100644
index 0000000..de14bc5
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/conf/xml.go
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package conf
+
+import (
+	"encoding/xml"
+	"io"
+	"log"
+)
+
+type configuration struct {
+	Properties []propertyXml `xml:"property"`
+}
+
+type propertyXml struct {
+	Name  string `xml:"name"`
+	Value string `xml:"value"`
+}
+
+// Parse an XML configuration file.
+func parseXml(reader io.Reader, m map[string]string) error {
+	dec := xml.NewDecoder(reader)
+	configurationXml := configuration{}
+	err := dec.Decode(&configurationXml)
+	if err != nil {
+		return err
+	}
+	props := configurationXml.Properties
+	for p := range props {
+		key := props[p].Name
+		value := props[p].Value
+		if key == "" {
+			log.Println("Warning: ignoring element with missing or empty <name>.")
+			continue
+		}
+		if value == "" {
+			log.Println("Warning: ignoring element with key " + key + " with missing or empty <value>.")
+			continue
+		}
+		//log.Printf("setting %s to %s\n", key, value)
+		m[key] = value
+	}
+	return nil
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go b/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go
new file mode 100644
index 0000000..b366ef1
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htrace/cmd.go
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/alecthomas/kingpin"
+	"io/ioutil"
+	"net/http"
+	"org/apache/htrace/common"
+	"org/apache/htrace/conf"
+	"os"
+)
+
+var RELEASE_VERSION string
+var GIT_VERSION string
+
+func main() {
+	// Load htraced configuration
+	cnf := conf.LoadApplicationConfig()
+
+	// Parse argv
+	app := kingpin.New("htrace", "The HTrace tracing utility.")
+	addr := app.Flag("addr", "Server address.").
+		Default(cnf.Get(conf.HTRACE_WEB_ADDRESS)).TCP()
+	version := app.Command("version", "Print the version of this program.")
+	serverInfo := app.Command("serverInfo", "Print information retrieved from an htraced server.")
+	findSpan := app.Command("findSpan", "Print information about a trace span with a given ID.")
+	findSpanId := findSpan.Flag("id", "Span ID to find, as a signed decimal 64-bit "+
+		"number").Required().Int64()
+	findChildren := app.Command("findChildren", "Print out the span IDs that are children of a given span ID.")
+	parentSpanId := findChildren.Flag("id", "Span ID to print children for, as a signed decimal 64-bit "+
+		"number").Required().Int64()
+	childLim := findChildren.Flag("lim", "Maximum number of child IDs to print.").Default("20").Int()
+
+	// Handle operation
+	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
+	case version.FullCommand():
+		os.Exit(printVersion())
+	case serverInfo.FullCommand():
+		os.Exit(printServerInfo((*addr).String()))
+	case findSpan.FullCommand():
+		os.Exit(doFindSpan((*addr).String(), *findSpanId))
+	case findChildren.FullCommand():
+		os.Exit(doFindChildren((*addr).String(), *parentSpanId, *childLim))
+	}
+
+	app.UsageErrorf(os.Stderr, "You must supply a command to run.")
+}
+
+// Print the version of the htrace binary.
+func printVersion() int {
+	fmt.Printf("Running htrace command version %s.\n", RELEASE_VERSION)
+	return 0
+}
+
+// Print information retrieved from an htraced server via /serverInfo
+func printServerInfo(restAddr string) int {
+	buf, err := makeRestRequest(restAddr, "serverInfo")
+	if err != nil {
+		fmt.Printf("%s\n", err.Error())
+		return 1
+	}
+	var info common.ServerInfo
+	err = json.Unmarshal(buf, &info)
+	if err != nil {
+		fmt.Printf("Error: error unmarshalling response body %s: %s\n",
+			string(buf), err.Error())
+		return 1
+	}
+	fmt.Printf("HTraced server version %s (%s)\n", info.ReleaseVersion, info.GitVersion)
+	return 0
+}
+
+// Print information about a trace span.
+func doFindSpan(restAddr string, sid int64) int {
+	buf, err := makeRestRequest(restAddr, fmt.Sprintf("findSid?sid=%016x", sid))
+	if err != nil {
+		fmt.Printf("%s\n", err.Error())
+		return 1
+	}
+	var span common.Span
+	err = json.Unmarshal(buf, &span)
+	if err != nil {
+		fmt.Printf("Error: error unmarshalling response body %s: %s\n",
+			string(buf), err.Error())
+		return 1
+	}
+	pbuf, err := json.MarshalIndent(span, "", "  ")
+	if err != nil {
+		fmt.Println("Error: error pretty-printing span to JSON: %s", err.Error())
+		return 1
+	}
+	fmt.Printf("%s\n", string(pbuf))
+	return 0
+}
+
+// Find information about the children of a span.
+func doFindChildren(restAddr string, sid int64, lim int) int {
+	buf, err := makeRestRequest(restAddr, fmt.Sprintf("findChildren?sid=%016x&lim=%d", sid, lim))
+	if err != nil {
+		fmt.Printf("%s\n", err.Error())
+		return 1
+	}
+	var spanIds []int64
+	err = json.Unmarshal(buf, &spanIds)
+	if err != nil {
+		fmt.Printf("Error: error unmarshalling response body %s: %s\n",
+			string(buf), err.Error())
+		return 1
+	}
+	pbuf, err := json.MarshalIndent(spanIds, "", "  ")
+	if err != nil {
+		fmt.Println("Error: error pretty-printing span IDs to JSON: %s", err.Error())
+		return 1
+	}
+	fmt.Printf("%s\n", string(pbuf))
+	return 0
+}
+
+// Print information retrieved from an htraced server via /serverInfo
+func makeRestRequest(restAddr string, reqName string) ([]byte, error) {
+	url := fmt.Sprintf("http://%s/%s", restAddr, reqName)
+	req, err := http.NewRequest("GET", url, nil)
+	client := &http.Client{}
+	resp, err := client.Do(req)
+	if err != nil {
+		return nil, errors.New(fmt.Sprintf("Error: error making http request to %s: %s\n", url,
+			err.Error()))
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode != http.StatusOK {
+		return nil, errors.New(fmt.Sprintf("Error: got bad response status from %s: %s\n", url, resp.Status))
+	}
+	var body []byte
+	body, err = ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, errors.New(fmt.Sprintf("Error: error reading response body: %s\n", err.Error()))
+	}
+	return body, nil
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
new file mode 100644
index 0000000..b43d4ce
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore.go
@@ -0,0 +1,444 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"bytes"
+	"encoding/gob"
+	"github.com/jmhodges/levigo"
+	"log"
+	"org/apache/htrace/common"
+	"org/apache/htrace/conf"
+	"os"
+	"strings"
+	"sync/atomic"
+	"syscall"
+)
+
+//
+// The data store code for HTraced.
+//
+// This code stores the trace spans.  We use levelDB here so that we don't have to store everything
+// in memory at all times.  The data is sharded across multiple levelDB databases in multiple
+// directories.  Normally, these multiple directories will be on multiple disk drives.
+//
+// The main emphasis in the HTraceD data store is on quickly and efficiently storing trace span data
+// coming from many daemons.  Durability is not as big a concern as in some data stores, since
+// losing a little bit of trace data if htraced goes down is not critical.  We use the "gob" package
+// for serialization.  We assume that there will be many more writes than reads.
+//
+// TODO: implement redundancy (storing data on more than 1 drive)
+// TODO: implement re-loading old span data
+//
+// Schema
+// m -> dataStoreMetadata
+// s[8-byte-big-endian-sid] -> SpanData
+// p[8-byte-big-endian-parent-sid][8-byte-big-endian-child-sid] -> {}
+// t[8-byte-big-endian-time][8-byte-big-endian-child-sid] -> {}
+//
+
+const DATA_STORE_VERSION = 1
+
+var EMPTY_BYTE_BUF []byte = []byte{}
+
+type Statistics struct {
+	NumSpansWritten uint64
+}
+
+func (stats *Statistics) IncrementWrittenSpans() {
+	atomic.AddUint64(&stats.NumSpansWritten, 1)
+}
+
+// Make a copy of the statistics structure, using atomic operations.
+func (stats *Statistics) Copy() *Statistics {
+	return &Statistics{
+		NumSpansWritten: atomic.LoadUint64(&stats.NumSpansWritten),
+	}
+}
+
+// Translate a span id into a leveldb key.
+func makeKey(tag byte, sid int64) []byte {
+	id := uint64(sid)
+	return []byte{
+		tag,
+		byte(0xff & (id >> 56)),
+		byte(0xff & (id >> 48)),
+		byte(0xff & (id >> 40)),
+		byte(0xff & (id >> 32)),
+		byte(0xff & (id >> 24)),
+		byte(0xff & (id >> 16)),
+		byte(0xff & (id >> 8)),
+		byte(0xff & (id >> 0)),
+	}
+}
+
+func keyToInt(key []byte) int64 {
+	var id uint64
+	id = (uint64(key[0]) << 56) |
+		(uint64(key[1]) << 48) |
+		(uint64(key[2]) << 40) |
+		(uint64(key[3]) << 32) |
+		(uint64(key[4]) << 24) |
+		(uint64(key[5]) << 16) |
+		(uint64(key[6]) << 8) |
+		(uint64(key[7]) << 0)
+	return int64(id)
+}
+
+func makeSecondaryKey(tag byte, first int64, second int64) []byte {
+	fir := uint64(first)
+	sec := uint64(second)
+	return []byte{
+		tag,
+		byte(0xff & (fir >> 56)),
+		byte(0xff & (fir >> 48)),
+		byte(0xff & (fir >> 40)),
+		byte(0xff & (fir >> 32)),
+		byte(0xff & (fir >> 24)),
+		byte(0xff & (fir >> 16)),
+		byte(0xff & (fir >> 8)),
+		byte(0xff & (fir >> 0)),
+		byte(0xff & (sec >> 56)),
+		byte(0xff & (sec >> 48)),
+		byte(0xff & (sec >> 40)),
+		byte(0xff & (sec >> 32)),
+		byte(0xff & (sec >> 24)),
+		byte(0xff & (sec >> 16)),
+		byte(0xff & (sec >> 8)),
+		byte(0xff & (sec >> 0)),
+	}
+}
+
+// A single directory containing a levelDB instance.
+type shard struct {
+	// The data store that this shard is part of
+	store *dataStore
+
+	// The LevelDB instance.
+	ldb *levigo.DB
+
+	// The path to the leveldb directory this shard is managing.
+	path string
+
+	// Incoming requests to write Spans.
+	incoming chan *common.Span
+
+	// The channel we will send a bool to when we exit.
+	exited chan bool
+}
+
+// Metadata about the DataStore.
+type dataStoreMetadata struct {
+	// The DataStore version.
+	Version int32
+}
+
+// Write the metadata key to a shard.
+func (shd *shard) WriteMetadata(meta *dataStoreMetadata) error {
+	w := new(bytes.Buffer)
+	encoder := gob.NewEncoder(w)
+	err := encoder.Encode(meta)
+	if err != nil {
+		return err
+	}
+	return shd.ldb.Put(shd.store.writeOpts, []byte("m"), w.Bytes())
+}
+
+// Process incoming spans for a shard.
+func (shd *shard) processIncoming() {
+	for {
+		span := <-shd.incoming
+		if span == nil {
+			log.Printf("Shard processor for %s exiting.", shd.path)
+			shd.exited <- true
+			return
+		}
+		err := shd.writeSpan(span)
+		if err != nil {
+			log.Fatal("Shard processor for %s got fatal error %s.", shd.path, err.Error())
+		}
+		//log.Printf("Shard processor for %s wrote span %s.", shd.path, span.ToJson())
+	}
+}
+
+func (shd *shard) writeSpan(span *common.Span) error {
+	batch := levigo.NewWriteBatch()
+	defer batch.Close()
+
+	// Add SpanData to batch.
+	spanDataBuf := new(bytes.Buffer)
+	spanDataEnc := gob.NewEncoder(spanDataBuf)
+	err := spanDataEnc.Encode(span.SpanData)
+	if err != nil {
+		return err
+	}
+	batch.Put(makeKey('s', span.Id.Val()), spanDataBuf.Bytes())
+
+	// Add this to the parent index.
+	for parentIdx := range span.Parents {
+		batch.Put(makeSecondaryKey('p', span.Parents[parentIdx].Val(), span.Id.Val()), EMPTY_BYTE_BUF)
+	}
+
+	// Add this to the timeline index.
+	batch.Put(makeSecondaryKey('t', span.Begin, span.Id.Val()), EMPTY_BYTE_BUF)
+
+	err = shd.ldb.Write(shd.store.writeOpts, batch)
+	if err != nil {
+		return err
+	}
+	shd.store.stats.IncrementWrittenSpans()
+	if shd.store.WrittenSpans != nil {
+		shd.store.WrittenSpans <- span
+	}
+	return nil
+}
+
+func (shd *shard) FindChildren(sid int64, childIds []int64, lim int32) ([]int64, int32, error) {
+	searchKey := makeKey('p', sid)
+	iter := shd.ldb.NewIterator(shd.store.readOpts)
+	defer iter.Close()
+	iter.Seek(searchKey)
+	for {
+		if !iter.Valid() {
+			break
+		}
+		if lim == 0 {
+			break
+		}
+		key := iter.Key()
+		if !bytes.HasPrefix(key, searchKey) {
+			break
+		}
+		id := keyToInt(key[9:])
+		childIds = append(childIds, id)
+		lim--
+		iter.Next()
+	}
+	return childIds, lim, nil
+}
+
+// Close a shard.
+func (shd *shard) Close() {
+	shd.incoming <- nil
+	log.Printf("Waiting for %s to exit...", shd.path)
+	if shd.exited != nil {
+		<-shd.exited
+	}
+	shd.ldb.Close()
+	log.Printf("Closed %s...", shd.path)
+}
+
+// The Data Store.
+type dataStore struct {
+	// The shards which manage our LevelDB instances.
+	shards []*shard
+
+	// I/O statistics for all shards.
+	stats Statistics
+
+	// The read options to use for LevelDB.
+	readOpts *levigo.ReadOptions
+
+	// The write options to use for LevelDB.
+	writeOpts *levigo.WriteOptions
+
+	// If non-null, a channel we will send spans to once we finish writing them.  This is only used
+	// for testing.
+	WrittenSpans chan *common.Span
+}
+
+func CreateDataStore(cnf *conf.Config, writtenSpans chan *common.Span) (*dataStore, error) {
+	// Get the configuration.
+	clearStored := cnf.GetBool(conf.HTRACE_DATA_STORE_CLEAR)
+	dirsStr := cnf.Get(conf.HTRACE_DATA_STORE_DIRECTORIES)
+	dirs := strings.Split(dirsStr, conf.PATH_LIST_SEP)
+
+	// If we return an error, close the store.
+	var err error
+	store := &dataStore{shards: []*shard{}, WrittenSpans: writtenSpans}
+	defer func() {
+		if err != nil {
+			store.Close()
+			store = nil
+		}
+	}()
+
+	store.readOpts = levigo.NewReadOptions()
+	store.readOpts.SetFillCache(true)
+	store.writeOpts = levigo.NewWriteOptions()
+	store.writeOpts.SetSync(false)
+
+	// Open all shards
+	for idx := range dirs {
+		path := dirs[idx] + conf.PATH_SEP + "db"
+		err := os.MkdirAll(path, 0777)
+		if err != nil {
+			e, ok := err.(*os.PathError)
+			if !ok || e.Err != syscall.EEXIST {
+				return nil, err
+			}
+			if !clearStored {
+				// TODO: implement re-opening saved data
+				log.Println("Error: path " + path + "already exists.")
+				return nil, err
+			} else {
+				err = os.RemoveAll(path)
+				if err != nil {
+					log.Println("Failed to create " + path + ": " + err.Error())
+					return nil, err
+				}
+				log.Println("Cleared " + path)
+			}
+		}
+		var shd *shard
+		shd, err = CreateShard(store, cnf, path)
+		if err != nil {
+			log.Printf("Error creating shard %s: %s", path, err.Error())
+			return nil, err
+		}
+		store.shards = append(store.shards, shd)
+	}
+	meta := &dataStoreMetadata{Version: DATA_STORE_VERSION}
+	for idx := range store.shards {
+		shd := store.shards[idx]
+		err := shd.WriteMetadata(meta)
+		if err != nil {
+			log.Println("Failed to write metadata to " + store.shards[idx].path + ": " + err.Error())
+			return nil, err
+		}
+		shd.exited = make(chan bool, 1)
+		go shd.processIncoming()
+	}
+	return store, nil
+}
+
+func CreateShard(store *dataStore, cnf *conf.Config, path string) (*shard, error) {
+	var shd *shard
+	//filter := levigo.NewBloomFilter(10)
+	//defer filter.Close()
+	openOpts := levigo.NewOptions()
+	defer openOpts.Close()
+	openOpts.SetCreateIfMissing(true)
+	//openOpts.SetFilterPolicy(filter)
+	ldb, err := levigo.Open(path, openOpts)
+	if err != nil {
+		log.Println("LevelDB failed to open " + path + ": " + err.Error())
+		return nil, err
+	}
+	defer func() {
+		if shd == nil {
+			ldb.Close()
+		}
+	}()
+	spanBufferSize := cnf.GetInt(conf.HTRACE_DATA_STORE_SPAN_BUFFER_SIZE)
+	shd = &shard{store: store, ldb: ldb, path: path,
+		incoming: make(chan *common.Span, spanBufferSize)}
+	log.Println("LevelDB opened " + path)
+	return shd, nil
+}
+
+func (store *dataStore) GetStatistics() *Statistics {
+	return store.stats.Copy()
+}
+
+// Close the DataStore.
+func (store *dataStore) Close() {
+	for idx := range store.shards {
+		store.shards[idx].Close()
+	}
+	if store.readOpts != nil {
+		store.readOpts.Close()
+	}
+	if store.writeOpts != nil {
+		store.writeOpts.Close()
+	}
+}
+
+// Get the index of the shard which stores the given spanId.
+func (store *dataStore) getShardIndex(spanId int64) int {
+	return int(uint64(spanId) % uint64(len(store.shards)))
+}
+
+func (store *dataStore) WriteSpan(span *common.Span) {
+	store.shards[store.getShardIndex(span.Id.Val())].incoming <- span
+}
+
+func (store *dataStore) FindSpan(sid int64) *common.Span {
+	return store.shards[store.getShardIndex(sid)].FindSpan(sid)
+}
+
+func (shd *shard) FindSpan(sid int64) *common.Span {
+	buf, err := shd.ldb.Get(shd.store.readOpts, makeKey('s', sid))
+	if err != nil {
+		if strings.Index(err.Error(), "NotFound:") != -1 {
+			return nil
+		}
+		log.Printf("Shard(%s): FindSpan(%d) error: %s\n",
+			shd.path, sid, err.Error())
+		return nil
+	}
+	r := bytes.NewBuffer(buf)
+	decoder := gob.NewDecoder(r)
+	data := common.SpanData{}
+	err = decoder.Decode(&data)
+	if err != nil {
+		log.Printf("Shard(%s): FindSpan(%d) decode error: %s\n",
+			shd.path, sid, err.Error())
+		return nil
+	}
+	// Gob encoding translates empty slices to nil.  Reverse this so that we're always dealing with
+	// non-nil slices.
+	if data.Parents == nil {
+		data.Parents = []common.SpanId{}
+	}
+	return &common.Span{Id: common.SpanId(sid), SpanData: data}
+}
+
+// Find the children of a given span id.
+func (store *dataStore) FindChildren(sid int64, lim int32) []int64 {
+	childIds := make([]int64, 0)
+	var err error
+
+	startIdx := store.getShardIndex(sid)
+	idx := startIdx
+	numShards := len(store.shards)
+	for {
+		if lim == 0 {
+			break
+		}
+		shd := store.shards[idx]
+		childIds, lim, err = shd.FindChildren(sid, childIds, lim)
+		if err != nil {
+			log.Printf("Shard(%s): FindChildren(%d) error: %s\n",
+				shd.path, sid, err.Error())
+		}
+		idx++
+		if idx >= numShards {
+			idx = 0
+		}
+		if idx == startIdx {
+			break
+		}
+	}
+	return childIds
+}
+
+//func (store *dataStore) FindByTimeRange(startTime int64, endTime int64, lim int32) []int64 {
+//}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
new file mode 100644
index 0000000..f037145
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/datastore_test.go
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"math/rand"
+	"org/apache/htrace/common"
+	"org/apache/htrace/test"
+	"sort"
+	"testing"
+)
+
+// Test creating and tearing down a datastore.
+func TestCreateDatastore(t *testing.T) {
+	htraceBld := &MiniHTracedBuilder{Name: "TestCreateDatastore", NumDataDirs: 3}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		t.Fatalf("failed to create datastore: %s", err.Error())
+	}
+	defer ht.Close()
+}
+
+var SIMPLE_TEST_SPANS []common.Span = []common.Span{
+	common.Span{Id: 1,
+		SpanData: common.SpanData{
+			Begin:       123,
+			End:         456,
+			Description: "getFileDescriptors",
+			TraceId:     999,
+			Parents:     []common.SpanId{},
+			ProcessId:   "firstd",
+		}},
+	common.Span{Id: 2,
+		SpanData: common.SpanData{
+			Begin:       125,
+			End:         200,
+			Description: "openFd",
+			TraceId:     999,
+			Parents:     []common.SpanId{1},
+			ProcessId:   "secondd",
+		}},
+	common.Span{Id: 3,
+		SpanData: common.SpanData{
+			Begin:       200,
+			End:         456,
+			Description: "passFd",
+			TraceId:     999,
+			Parents:     []common.SpanId{1},
+			ProcessId:   "thirdd",
+		}},
+}
+
+func createSpans(spans []common.Span, store *dataStore) {
+	for idx := range spans {
+		store.WriteSpan(&spans[idx])
+	}
+	// Wait the spans to be created
+	for i := 0; i < 3; i++ {
+		<-store.WrittenSpans
+	}
+}
+
+// Test creating a datastore and adding some spans.
+func TestDatastoreWriteAndRead(t *testing.T) {
+	t.Parallel()
+	htraceBld := &MiniHTracedBuilder{Name: "TestDatastoreWriteAndRead",
+		WrittenSpans: make(chan *common.Span, 100)}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		panic(err)
+	}
+	defer ht.Close()
+	createSpans(SIMPLE_TEST_SPANS, ht.Store)
+	if ht.Store.GetStatistics().NumSpansWritten < uint64(len(SIMPLE_TEST_SPANS)) {
+		t.Fatal()
+	}
+	span := ht.Store.FindSpan(1)
+	if span == nil {
+		t.Fatal()
+	}
+	if span.Id != 1 {
+		t.Fatal()
+	}
+	common.ExpectSpansEqual(t, &SIMPLE_TEST_SPANS[0], span)
+	children := ht.Store.FindChildren(1, 1)
+	if len(children) != 1 {
+		t.Fatalf("expected 1 child, but got %d\n", len(children))
+	}
+	children = ht.Store.FindChildren(1, 2)
+	if len(children) != 2 {
+		t.Fatalf("expected 2 children, but got %d\n", len(children))
+	}
+	sort.Sort(common.Int64Slice(children))
+	if children[0] != 2 {
+		t.Fatal()
+	}
+	if children[1] != 3 {
+		t.Fatal()
+	}
+}
+
+func BenchmarkDatastoreWrites(b *testing.B) {
+	htraceBld := &MiniHTracedBuilder{Name: "BenchmarkDatastoreWrites",
+		WrittenSpans: make(chan *common.Span, b.N)}
+	ht, err := htraceBld.Build()
+	if err != nil {
+		panic(err)
+	}
+	defer ht.Close()
+	rnd := rand.New(rand.NewSource(1))
+	allSpans := make([]*common.Span, b.N)
+	// Write many random spans.
+	for n := 0; n < b.N; n++ {
+		span := test.NewRandomSpan(rnd, allSpans[0:n])
+		ht.Store.WriteSpan(span)
+		allSpans[n] = span
+	}
+	// Wait for all the spans to be written.
+	for n := 0; n < b.N; n++ {
+		<-ht.Store.WrittenSpans
+	}
+	spansWritten := ht.Store.GetStatistics().NumSpansWritten
+	if spansWritten < uint64(b.N) {
+		b.Fatal("incorrect statistics: expected %d spans to be written, but only got %d",
+			b.N, spansWritten)
+	}
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/htraced.go b/htrace-core/src/go/src/org/apache/htrace/htraced/htraced.go
new file mode 100644
index 0000000..403b1f9
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/htraced.go
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"log"
+	"org/apache/htrace/conf"
+)
+
+var RELEASE_VERSION string
+var GIT_VERSION string
+
+func main() {
+	cnf := conf.LoadApplicationConfig()
+	store, err := CreateDataStore(cnf, nil)
+	if err != nil {
+		log.Fatalf("Error creating datastore: %s\n", err.Error())
+	}
+	startRestServer(cnf, store)
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/mini_htraced.go b/htrace-core/src/go/src/org/apache/htrace/htraced/mini_htraced.go
new file mode 100644
index 0000000..43d7e23
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/mini_htraced.go
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"org/apache/htrace/common"
+	"org/apache/htrace/conf"
+	"os"
+	"strings"
+)
+
+//
+// MiniHTraceD is used in unit tests to set up a daemon with certain settings.
+// It takes care of things like creating and cleaning up temporary directories.
+//
+
+// The default number of managed data directories to use.
+const DEFAULT_NUM_DATA_DIRS = 2
+
+// Builds a MiniHTraced object.
+type MiniHTracedBuilder struct {
+	// The name of the MiniHTraced to build.  This shows up in the test directory name and some
+	// other places.
+	Name string
+
+	// The configuration values to use for the MiniHTraced.
+	// If ths is nil, we use the default configuration for everything.
+	Cnf map[string]string
+
+	// The number of managed data directories to create.
+	// If this is 0, it defaults to DEFAULT_NUM_DATA_DIRS.
+	NumDataDirs int
+
+	// If non-null, the WrittenSpans channel to use when creating the DataStore.
+	WrittenSpans chan *common.Span
+}
+
+type MiniHTraced struct {
+	Name     string
+	Cnf      *conf.Config
+	DataDirs []string
+	Store    *dataStore
+}
+
+func (bld *MiniHTracedBuilder) Build() (*MiniHTraced, error) {
+	var err error
+	var store *dataStore
+	if bld.Name == "" {
+		bld.Name = "HTraceTest"
+	}
+	if bld.Cnf == nil {
+		bld.Cnf = make(map[string]string)
+	}
+	if bld.NumDataDirs == 0 {
+		bld.NumDataDirs = DEFAULT_NUM_DATA_DIRS
+	}
+	dataDirs := make([]string, bld.NumDataDirs)
+	defer func() {
+		if err != nil {
+			if store != nil {
+				store.Close()
+			}
+			for idx := range dataDirs {
+				if dataDirs[idx] != "" {
+					os.RemoveAll(dataDirs[idx])
+				}
+			}
+		}
+	}()
+	for idx := range dataDirs {
+		dataDirs[idx], err = ioutil.TempDir(os.TempDir(),
+			fmt.Sprintf("%s%d", bld.Name, idx+1))
+		if err != nil {
+			return nil, err
+		}
+	}
+	bld.Cnf[conf.HTRACE_DATA_STORE_DIRECTORIES] = strings.Join(dataDirs, conf.PATH_LIST_SEP)
+	cnfBld := conf.Builder{Values: bld.Cnf, Defaults: conf.DEFAULTS}
+	cnf, err := cnfBld.Build()
+	if err != nil {
+		return nil, err
+	}
+	store, err = CreateDataStore(cnf, bld.WrittenSpans)
+	if err != nil {
+		return nil, err
+	}
+	return &MiniHTraced{
+		Cnf:      cnf,
+		DataDirs: dataDirs,
+		Store:    store,
+	}, nil
+}
+
+func (ht *MiniHTraced) Close() {
+	ht.Store.Close()
+	for idx := range ht.DataDirs {
+		os.RemoveAll(ht.DataDirs[idx])
+	}
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
new file mode 100644
index 0000000..7eb8870
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package main
+
+import (
+	"encoding/json"
+	"log"
+	"net/http"
+	"org/apache/htrace/common"
+	"org/apache/htrace/conf"
+	"org/apache/htrace/resource"
+	"strconv"
+	"strings"
+)
+
+type serverInfoHandler struct {
+}
+
+func (handler *serverInfoHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	version := common.ServerInfo{ReleaseVersion: RELEASE_VERSION,
+		GitVersion: GIT_VERSION}
+	buf, err := json.Marshal(&version)
+	if err != nil {
+		log.Printf("error marshalling ServerInfo: %s\n", err.Error())
+		w.WriteHeader(http.StatusInternalServerError)
+		return
+	}
+	w.Write(buf)
+}
+
+type dataStoreHandler struct {
+	store *dataStore
+}
+
+func (hand *dataStoreHandler) getReqField64(fieldName string, w http.ResponseWriter,
+	req *http.Request) (int64, bool) {
+	str := req.FormValue(fieldName)
+	if str == "" {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("No " + fieldName + " specified."))
+		return -1, false
+	}
+	val, err := strconv.ParseUint(str, 16, 64)
+	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("Error parsing " + fieldName + ": " + err.Error()))
+		return -1, false
+	}
+	return int64(val), true
+}
+
+func (hand *dataStoreHandler) getReqField32(fieldName string, w http.ResponseWriter,
+	req *http.Request) (int32, bool) {
+	str := req.FormValue(fieldName)
+	if str == "" {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("No " + fieldName + " specified."))
+		return -1, false
+	}
+	val, err := strconv.ParseUint(str, 16, 32)
+	if err != nil {
+		w.WriteHeader(http.StatusBadRequest)
+		w.Write([]byte("Error parsing " + fieldName + ": " + err.Error()))
+		return -1, false
+	}
+	return int32(val), true
+}
+
+type findSidHandler struct {
+	dataStoreHandler
+}
+
+func (hand *findSidHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	req.ParseForm()
+	sid, ok := hand.getReqField64("sid", w, req)
+	if !ok {
+		return
+	}
+	span := hand.store.FindSpan(sid)
+	if span == nil {
+		w.WriteHeader(http.StatusNoContent)
+		return
+	}
+	w.Write(span.ToJson())
+}
+
+type findChildrenHandler struct {
+	dataStoreHandler
+}
+
+func (hand *findChildrenHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	req.ParseForm()
+	sid, ok := hand.getReqField64("sid", w, req)
+	if !ok {
+		return
+	}
+	var lim int32
+	lim, ok = hand.getReqField32("lim", w, req)
+	if !ok {
+		return
+	}
+	children := hand.store.FindChildren(sid, lim)
+	if len(children) == 0 {
+		w.WriteHeader(http.StatusNoContent)
+		return
+	}
+	jbytes, err := json.Marshal(children)
+	if err != nil {
+		panic(err)
+	}
+	w.Write(jbytes)
+}
+
+type defaultServeHandler struct {
+}
+
+func (hand *defaultServeHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	ident := strings.TrimLeft(req.URL.Path, "/")
+	ident = strings.Replace(ident, "/", "__", -1)
+	rsc := resource.Catalog[ident]
+	if rsc == "" {
+		log.Printf("failed to find entry for %s\n", ident)
+		w.WriteHeader(http.StatusNotFound)
+		return
+	}
+	w.Write([]byte(rsc))
+}
+
+func startRestServer(cnf *conf.Config, store *dataStore) {
+	mux := http.NewServeMux()
+
+	serverInfoH := &serverInfoHandler{}
+	mux.Handle("/serverInfo", serverInfoH)
+
+	findSidH := &findSidHandler{dataStoreHandler: dataStoreHandler{store: store}}
+	mux.Handle("/findSid", findSidH)
+
+	findChildrenH := &findChildrenHandler{dataStoreHandler: dataStoreHandler{store: store}}
+	mux.Handle("/findChildren", findChildrenH)
+
+	defaultServeH := &defaultServeHandler{}
+	mux.Handle("/", defaultServeH)
+
+	http.ListenAndServe(cnf.Get(conf.HTRACE_WEB_ADDRESS), mux)
+	log.Println("Started REST server...")
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/resource/catalog.go b/htrace-core/src/go/src/org/apache/htrace/resource/catalog.go
new file mode 100644
index 0000000..5b71f88
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/resource/catalog.go
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package resource
+
+// Map containing all resources.
+var Catalog map[string]string = make(map[string]string)
+
+// Function called by generated code to add a resource to the catalog.
+func addResource(key, val string) string {
+	Catalog[key] = val
+	return key
+}
diff --git a/htrace-core/src/go/src/org/apache/htrace/test/random.go b/htrace-core/src/go/src/org/apache/htrace/test/random.go
new file mode 100644
index 0000000..d10e2f9
--- /dev/null
+++ b/htrace-core/src/go/src/org/apache/htrace/test/random.go
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package test
+
+import (
+	"fmt"
+	"math/rand"
+	"org/apache/htrace/common"
+)
+
+func NonZeroRand64(rnd *rand.Rand) int64 {
+	for {
+		r := rnd.Int63()
+		if r == 0 {
+			continue
+		}
+		if rnd.Intn(1) != 0 {
+			return -r
+		}
+		return r
+	}
+}
+
+func NonZeroRand32(rnd *rand.Rand) int32 {
+	for {
+		r := rnd.Int31()
+		if r == 0 {
+			continue
+		}
+		if rnd.Intn(1) != 0 {
+			return -r
+		}
+		return r
+	}
+}
+
+// Create a random span.
+func NewRandomSpan(rnd *rand.Rand, potentialParents []*common.Span) *common.Span {
+	parents := []common.SpanId{}
+	if potentialParents != nil {
+		parentIdx := rnd.Intn(len(potentialParents) + 1)
+		if parentIdx < len(potentialParents) {
+			parents = []common.SpanId{potentialParents[parentIdx].Id}
+		}
+	}
+	return &common.Span{Id: common.SpanId(NonZeroRand64(rnd)),
+		SpanData: common.SpanData{
+			Begin:       NonZeroRand64(rnd),
+			End:         NonZeroRand64(rnd),
+			Description: "getFileDescriptors",
+			TraceId:     common.SpanId(NonZeroRand64(rnd)),
+			Parents:     parents,
+			ProcessId:   fmt.Sprintf("process%d", NonZeroRand32(rnd)),
+		}}
+}
diff --git a/htrace-core/src/main/java/org/htrace/HTraceConfiguration.java b/htrace-core/src/main/java/org/apache/htrace/HTraceConfiguration.java
similarity index 76%
rename from htrace-core/src/main/java/org/htrace/HTraceConfiguration.java
rename to htrace-core/src/main/java/org/apache/htrace/HTraceConfiguration.java
index 7ad608f..f4896e9 100644
--- a/htrace-core/src/main/java/org/htrace/HTraceConfiguration.java
+++ b/htrace-core/src/main/java/org/apache/htrace/HTraceConfiguration.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -30,10 +30,35 @@ public abstract class HTraceConfiguration {
 
   private static final Log LOG = LogFactory.getLog(HTraceConfiguration.class);
 
+  private static final Map<String, String> EMPTY_MAP = new HashMap<String, String>(1);
+
+  /**
+   * An empty HTrace configuration.
+   */
+  public static final HTraceConfiguration EMPTY = fromMap(EMPTY_MAP);
+
+  /**
+   * Create an HTrace configuration from a map.
+   *
+   * @param conf    The map to create the configuration from.
+   * @return        The new configuration.
+   */
   public static HTraceConfiguration fromMap(Map<String, String> conf) {
     return new MapConf(conf);
   }
 
+  static HTraceConfiguration fromKeyValuePairs(String... pairs) {
+    if ((pairs.length % 2) != 0) {
+      throw new RuntimeException("You must specify an equal number of keys " +
+          "and values.");
+    }
+    Map<String, String> conf = new HashMap<String, String>();
+    for (int i = 0; i < pairs.length; i+=2) {
+      conf.put(pairs[i], pairs[i + 1]);
+    }
+    return new MapConf(conf);
+  }
+
   public abstract String get(String key);
 
   public abstract String get(String key, String defaultValue);
diff --git a/htrace-core/src/main/java/org/htrace/NullScope.java b/htrace-core/src/main/java/org/apache/htrace/NullScope.java
similarity index 97%
rename from htrace-core/src/main/java/org/htrace/NullScope.java
rename to htrace-core/src/main/java/org/apache/htrace/NullScope.java
index 1f90156..32963a8 100644
--- a/htrace-core/src/main/java/org/htrace/NullScope.java
+++ b/htrace-core/src/main/java/org/apache/htrace/NullScope.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 /**
  * Singleton instance representing an empty {@link TraceScope}.
diff --git a/htrace-core/src/main/java/org/htrace/Sampler.java b/htrace-core/src/main/java/org/apache/htrace/Sampler.java
similarity index 79%
rename from htrace-core/src/main/java/org/htrace/Sampler.java
rename to htrace-core/src/main/java/org/apache/htrace/Sampler.java
index 08c520d..45d8872 100644
--- a/htrace-core/src/main/java/org/htrace/Sampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/Sampler.java
@@ -14,10 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
-import org.htrace.impl.AlwaysSampler;
-import org.htrace.impl.NeverSampler;
+import org.apache.htrace.impl.AlwaysSampler;
+import org.apache.htrace.impl.NeverSampler;
 
 /**
  * Extremely simple callback to determine the frequency that an action should be
@@ -31,17 +31,20 @@ import org.htrace.impl.NeverSampler;
  * <p/>
  * For the example above, the next(T info) function may look like this
  * <p/>
- * public boolean next(T info){
- * if (info == null) {
- * return false;
- * } else if (info.getName().equals("get")) {
- * return Math.random() > 0.5;
- * } else if (info.getName().equals("put")) {
- * return Math.random() > 0.25;
- * } else {
- * return false;
- * }
+ * <pre>
+ * <code>public boolean next(T info) {
+ *   if (info == null) {
+ *     return false;
+ *   } else if (info.getName().equals("get")) {
+ *     return Math.random() > 0.5;
+ *   } else if (info.getName().equals("put")) {
+ *     return Math.random() > 0.25;
+ *   } else {
+ *     return false;
+ *   }
  * }
+ * </code>
+ * </pre>
  * This would trace 50% of all gets, 75% of all puts and would not trace any other requests.
  */
 public interface Sampler<T> {
diff --git a/htrace-core/src/main/java/org/apache/htrace/SamplerBuilder.java b/htrace-core/src/main/java/org/apache/htrace/SamplerBuilder.java
new file mode 100644
index 0000000..5d90e48
--- /dev/null
+++ b/htrace-core/src/main/java/org/apache/htrace/SamplerBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace;
+
+import java.lang.reflect.Constructor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.htrace.impl.AlwaysSampler;
+import org.apache.htrace.impl.NeverSampler;
+
+/**
+ * A {@link Sampler} builder. It reads a {@link Sampler} class name from the provided
+ * configuration using the {@link #SAMPLER_CONF_KEY} key. Unqualified class names
+ * are interpreted as members of the {@code org.apache.htrace.impl} package. The {@link #build()}
+ * method constructs an instance of that class, initialized with the same configuration.
+ */
+public class SamplerBuilder {
+
+  // TODO: should follow the same API as SpanReceiverBuilder
+
+  public final static String SAMPLER_CONF_KEY = "sampler";
+  private final static String DEFAULT_PACKAGE = "org.apache.htrace.impl";
+  private final static ClassLoader classLoader =
+      SamplerBuilder.class.getClassLoader();
+  private final HTraceConfiguration conf;
+  private static final Log LOG = LogFactory.getLog(SamplerBuilder.class);
+
+  public SamplerBuilder(HTraceConfiguration conf) {
+    this.conf = conf;
+  }
+
+  public Sampler build() {
+    String str = conf.get(SAMPLER_CONF_KEY);
+    if (str.isEmpty()) {
+      return NeverSampler.INSTANCE;
+    }
+    if (!str.contains(".")) {
+      str = DEFAULT_PACKAGE + "." + str;
+    }
+    Class cls = null;
+    try {
+      cls = classLoader.loadClass(str);
+    } catch (ClassNotFoundException e) {
+      LOG.error("SamplerBuilder cannot find sampler class " + str +
+          ": falling back on NeverSampler.");
+      return NeverSampler.INSTANCE;
+    }
+    Constructor<Sampler> ctor = null;
+    try {
+      ctor = cls.getConstructor(HTraceConfiguration.class);
+    } catch (NoSuchMethodException e) {
+      LOG.error("SamplerBuilder cannot find a constructor for class " + str +
+          "which takes an HTraceConfiguration.  Falling back on " +
+          "NeverSampler.");
+      return NeverSampler.INSTANCE;
+    }
+    try {
+      return ctor.newInstance(conf);
+    } catch (ReflectiveOperationException e) {
+      LOG.error("SamplerBuilder reflection error when constructing " + str +
+          ".  Falling back on NeverSampler.", e);
+      return NeverSampler.INSTANCE;
+    } catch (Throwable e) {
+      LOG.error("SamplerBuilder constructor error when constructing " + str +
+          ".  Falling back on NeverSampler.", e);
+      return NeverSampler.INSTANCE;
+    }
+  }
+}
diff --git a/htrace-core/src/main/java/org/htrace/Span.java b/htrace-core/src/main/java/org/apache/htrace/Span.java
similarity index 55%
rename from htrace-core/src/main/java/org/htrace/Span.java
rename to htrace-core/src/main/java/org/apache/htrace/Span.java
index d7d0d9a..b8af10d 100644
--- a/htrace-core/src/main/java/org/htrace/Span.java
+++ b/htrace-core/src/main/java/org/apache/htrace/Span.java
@@ -14,8 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 
@@ -27,6 +33,7 @@ import java.util.Map;
  * Spans form a tree structure with the parent relationship. The first span in a
  * trace has no parent span.
  */
+ at JsonSerialize(using = Span.SpanSerializer.class)
 public interface Span {
   public static final long ROOT_SPAN_ID = 0x74ace;
 
@@ -81,8 +88,8 @@ public interface Span {
   String toString();
 
   /**
-   * Return the pseudo-unique (random) number of the parent span, returns
-   * ROOT_SPAN_ID if this is the root span
+   * Return the pseudo-unique (random) number of the first parent span, returns
+   * ROOT_SPAN_ID if there are no parents.
    */
   long getParentId();
 
@@ -113,4 +120,50 @@ public interface Span {
    * @return
    */
   String getProcessId();
+
+  /**
+   * Serialize to Json
+   */
+  String toJson();
+
+  public static class SpanSerializer extends JsonSerializer<Span> {
+    @Override
+    public void serialize(Span span, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException {
+      jgen.writeStartObject();
+      jgen.writeStringField("i", String.format("%016x", span.getTraceId()));
+      jgen.writeStringField("s", String.format("%016x", span.getSpanId()));
+      jgen.writeNumberField("b", span.getStartTimeMillis());
+      jgen.writeNumberField("e", span.getStopTimeMillis());
+      jgen.writeStringField("d", span.getDescription());
+      jgen.writeStringField("r", span.getProcessId());
+      jgen.writeArrayFieldStart("p");
+      if (span.getParentId() != ROOT_SPAN_ID) {
+        jgen.writeString(String.format("%016x", span.getParentId()));
+      }
+      jgen.writeEndArray();
+      Map<byte[], byte[]> traceInfoMap = span.getKVAnnotations();
+      if (!traceInfoMap.isEmpty()) {
+        jgen.writeObjectFieldStart("n");
+        for (Map.Entry<byte[], byte[]> e : traceInfoMap.entrySet()) {
+          jgen.writeStringField(new String(e.getKey(), "UTF-8"),
+              new String(e.getValue(), "UTF-8"));
+        }
+        jgen.writeEndObject();
+      }
+      List<TimelineAnnotation> timelineAnnotations =
+          span.getTimelineAnnotations();
+      if (!timelineAnnotations.isEmpty()) {
+        jgen.writeArrayFieldStart("t");
+        for (TimelineAnnotation tl : timelineAnnotations) {
+          jgen.writeStartObject();
+          jgen.writeNumberField("t", tl.getTime());
+          jgen.writeStringField("m", tl.getMessage());
+          jgen.writeEndObject();
+        }
+        jgen.writeEndArray();
+      }
+      jgen.writeEndObject();
+    }
+  }
 }
diff --git a/htrace-core/src/main/java/org/htrace/SpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/SpanReceiver.java
similarity index 70%
rename from htrace-core/src/main/java/org/htrace/SpanReceiver.java
rename to htrace-core/src/main/java/org/apache/htrace/SpanReceiver.java
index 65cfb45..7ae157b 100644
--- a/htrace-core/src/main/java/org/htrace/SpanReceiver.java
+++ b/htrace-core/src/main/java/org/apache/htrace/SpanReceiver.java
@@ -14,29 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 
 import java.io.Closeable;
 
 
 /**
- * The collector within a process that is the destination of Spans when a trace
- * is running.
+ * The collector within a process that is the destination of Spans when a trace is running.
+ * {@code SpanReceiver} implementations are expected to provide a constructor with the signature
+ * <p>
+ * <pre>
+ * <code>public SpanReceiverImpl(HTraceConfiguration)</code>
+ * </pre>
+ * The helper class {@link org.apache.htrace.SpanReceiverBuilder} provides convenient factory
+ * methods for creating {@code SpanReceiver} instances from configuration.
+ * @see org.apache.htrace.SpanReceiverBuilder
  */
 public interface SpanReceiver extends Closeable {
-
-  /**
-   * Hosts of SpanReceivers should call this method to provide
-   * configuration to SpanReceivers after creating them.
-   */
-  public void configure(HTraceConfiguration conf);
-
   /**
    * Called when a Span is stopped and can now be stored.
-   *
-   * @param span
    */
   public void receiveSpan(Span span);
-
 }
diff --git a/htrace-core/src/main/java/org/apache/htrace/SpanReceiverBuilder.java b/htrace-core/src/main/java/org/apache/htrace/SpanReceiverBuilder.java
new file mode 100644
index 0000000..cf6b050
--- /dev/null
+++ b/htrace-core/src/main/java/org/apache/htrace/SpanReceiverBuilder.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace;
+
+import java.lang.reflect.Constructor;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A {@link SpanReceiver} builder. It reads a {@link SpanReceiver} class name from the provided
+ * configuration using the {@link #SPAN_RECEIVER_CONF_KEY} key. Unqualified class names
+ * are interpreted as members of the {@code org.apache.htrace.impl} package. The {@link #build()}
+ * method constructs an instance of that class, initialized with the same configuration.
+ */
+public class SpanReceiverBuilder {
+  static final Log LOG = LogFactory.getLog(SpanReceiverBuilder.class);
+
+  public final static String SPAN_RECEIVER_CONF_KEY = "span.receiver";
+  private final static String DEFAULT_PACKAGE = "org.apache.htrace.impl";
+  private final static ClassLoader classLoader =
+      SpanReceiverBuilder.class.getClassLoader();
+  private final HTraceConfiguration conf;
+  private boolean logErrors;
+  private String spanReceiverClass;
+
+  public SpanReceiverBuilder(HTraceConfiguration conf) {
+    this.conf = conf;
+    reset();
+  }
+
+  /**
+   * Set this builder back to defaults. Any previous calls to {@link #spanReceiverClass(String)}
+   * are overridden by the value provided by configuration.
+   * @return This instance
+   */
+  public SpanReceiverBuilder reset() {
+    this.logErrors = true;
+    this.spanReceiverClass = this.conf.get(SPAN_RECEIVER_CONF_KEY);
+    return this;
+  }
+
+  /**
+   * Override the {@code SpanReceiver} class name provided in configuration with a new value.
+   * @return This instance
+   */
+  public SpanReceiverBuilder spanReceiverClass(final String spanReceiverClass) {
+    this.spanReceiverClass = spanReceiverClass;
+    return this;
+  }
+
+  /**
+   * Configure whether we should log errors during build().
+   * @return This instance
+   */
+  public SpanReceiverBuilder logErrors(boolean logErrors) {
+    this.logErrors = logErrors;
+    return this;
+  }
+
+  private void logError(String errorStr) {
+    if (!logErrors) {
+      return;
+    }
+    LOG.error(errorStr);
+  }
+
+  private void logError(String errorStr, Throwable e) {
+    if (!logErrors) {
+      return;
+    }
+    LOG.error(errorStr, e);
+  }
+
+  public SpanReceiver build() {
+    if ((this.spanReceiverClass == null) ||
+        this.spanReceiverClass.isEmpty()) {
+      return null;
+    }
+    String str = spanReceiverClass;
+    if (!str.contains(".")) {
+      str = DEFAULT_PACKAGE + "." + str;
+    }
+    Class cls = null;
+    try {
+      cls = classLoader.loadClass(str);
+    } catch (ClassNotFoundException e) {
+      logError("SpanReceiverBuilder cannot find SpanReceiver class " + str +
+          ": disabling span receiver.");
+      return null;
+    }
+    Constructor<SpanReceiver> ctor = null;
+    try {
+      ctor = cls.getConstructor(HTraceConfiguration.class);
+    } catch (NoSuchMethodException e) {
+      logError("SpanReceiverBuilder cannot find a constructor for class " +
+          str + "which takes an HTraceConfiguration.  Disabling span " +
+          "receiver.");
+      return null;
+    }
+    try {
+      return ctor.newInstance(conf);
+    } catch (ReflectiveOperationException e) {
+      logError("SpanReceiverBuilder reflection error when constructing " + str +
+          ".  Disabling span receiver.", e);
+      return null;
+    } catch (Throwable e) {
+      logError("SpanReceiverBuilder constructor error when constructing " + str +
+          ".  Disabling span receiver.", e);
+      return null;
+    }
+  }
+}
\ No newline at end of file
diff --git a/htrace-core/src/main/java/org/htrace/TimelineAnnotation.java b/htrace-core/src/main/java/org/apache/htrace/TimelineAnnotation.java
similarity index 97%
rename from htrace-core/src/main/java/org/htrace/TimelineAnnotation.java
rename to htrace-core/src/main/java/org/apache/htrace/TimelineAnnotation.java
index 265d209..d0ae675 100644
--- a/htrace-core/src/main/java/org/htrace/TimelineAnnotation.java
+++ b/htrace-core/src/main/java/org/apache/htrace/TimelineAnnotation.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 public class TimelineAnnotation {
   private final long time;
diff --git a/htrace-core/src/main/java/org/htrace/Trace.java b/htrace-core/src/main/java/org/apache/htrace/Trace.java
similarity index 96%
rename from htrace-core/src/main/java/org/htrace/Trace.java
rename to htrace-core/src/main/java/org/apache/htrace/Trace.java
index 8b1255b..61dfdfa 100644
--- a/htrace-core/src/main/java/org/htrace/Trace.java
+++ b/htrace-core/src/main/java/org/apache/htrace/Trace.java
@@ -14,12 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
-import org.htrace.impl.MilliSpan;
-import org.htrace.impl.TrueIfTracingSampler;
-import org.htrace.wrappers.TraceCallable;
-import org.htrace.wrappers.TraceRunnable;
+import org.apache.htrace.impl.MilliSpan;
+import org.apache.htrace.impl.TrueIfTracingSampler;
+import org.apache.htrace.wrappers.TraceCallable;
+import org.apache.htrace.wrappers.TraceRunnable;
 
 import java.security.SecureRandom;
 import java.util.Random;
diff --git a/htrace-core/src/main/java/org/htrace/TraceInfo.java b/htrace-core/src/main/java/org/apache/htrace/TraceInfo.java
similarity index 97%
rename from htrace-core/src/main/java/org/htrace/TraceInfo.java
rename to htrace-core/src/main/java/org/apache/htrace/TraceInfo.java
index 438289d..9e7d74a 100644
--- a/htrace-core/src/main/java/org/htrace/TraceInfo.java
+++ b/htrace-core/src/main/java/org/apache/htrace/TraceInfo.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 
 public class TraceInfo {
diff --git a/htrace-core/src/main/java/org/htrace/TraceScope.java b/htrace-core/src/main/java/org/apache/htrace/TraceScope.java
similarity index 98%
rename from htrace-core/src/main/java/org/htrace/TraceScope.java
rename to htrace-core/src/main/java/org/apache/htrace/TraceScope.java
index f001c52..7773aad 100644
--- a/htrace-core/src/main/java/org/htrace/TraceScope.java
+++ b/htrace-core/src/main/java/org/apache/htrace/TraceScope.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 import java.io.Closeable;
 
diff --git a/htrace-core/src/main/java/org/apache/htrace/TraceTree.java b/htrace-core/src/main/java/org/apache/htrace/TraceTree.java
new file mode 100644
index 0000000..a005c55
--- /dev/null
+++ b/htrace-core/src/main/java/org/apache/htrace/TraceTree.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace;
+
+import org.apache.htrace.impl.MilliSpan;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Used to create the graph formed by spans.
+ */
+public class TraceTree {
+  public static final Log LOG = LogFactory.getLog(Tracer.class);
+
+  public static class SpansByParent {
+    private static Comparator<Span> COMPARATOR =
+        new Comparator<Span>() {
+          @Override
+          public int compare(Span a, Span b) {
+            if (a.getParentId() < b.getParentId()) {
+              return -1;
+            } else if (a.getParentId() > b.getParentId()) {
+              return 1;
+            } else if (a.getSpanId() < b.getSpanId()) {
+              return -1;
+            } else if (a.getSpanId() > b.getSpanId()) {
+              return 1;
+            } else {
+              return 0;
+            }
+          }
+        };
+
+    private final TreeSet<Span> treeSet;
+
+    SpansByParent(Collection<Span> spans) {
+      TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR);
+      for (Span span : spans) {
+        treeSet.add(span);
+      }
+      this.treeSet = treeSet;
+    }
+
+    public List<Span> find(long parentId) {
+      List<Span> spans = new ArrayList<Span>();
+      Span span = new MilliSpan("", Long.MIN_VALUE,
+          parentId, Long.MIN_VALUE, "");
+      while (true) {
+        span = treeSet.higher(span);
+        if (span == null) {
+          break;
+        }
+        if (span.getParentId() != parentId) {
+          break;
+        }
+        spans.add(span);
+      }
+      return spans;
+    }
+
+    public Iterator<Span> iterator() {
+      return Collections.unmodifiableSortedSet(treeSet).iterator();
+    }
+  }
+
+  public static class SpansByProcessId {
+    private static Comparator<Span> COMPARATOR =
+        new Comparator<Span>() {
+          @Override
+          public int compare(Span a, Span b) {
+            int cmp = a.getProcessId().compareTo(b.getProcessId());
+            if (cmp != 0) {
+              return cmp;
+            } else if (a.getSpanId() < b.getSpanId()) {
+              return -1;
+            } else if (a.getSpanId() > b.getSpanId()) {
+              return 1;
+            } else {
+              return 0;
+            }
+          }
+        };
+
+    private final TreeSet<Span> treeSet;
+
+    SpansByProcessId(Collection<Span> spans) {
+      TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR);
+      for (Span span : spans) {
+        treeSet.add(span);
+      }
+      this.treeSet = treeSet;
+    }
+
+    public List<Span> find(String processId) {
+      List<Span> spans = new ArrayList<Span>();
+      Span span = new MilliSpan("", Long.MIN_VALUE,
+          Long.MIN_VALUE, Long.MIN_VALUE, processId);
+      while (true) {
+        span = treeSet.higher(span);
+        if (span == null) {
+          break;
+        }
+        if (span.getProcessId().equals(processId)) {
+          break;
+        }
+        spans.add(span);
+      }
+      return spans;
+    }
+
+    public Iterator<Span> iterator() {
+      return Collections.unmodifiableSortedSet(treeSet).iterator();
+    }
+  }
+
+  private final SpansByParent spansByParent;
+  private final SpansByProcessId spansByProcessId;
+
+  /**
+   * Create a new TraceTree
+   *
+   * @param spans The collection of spans to use to create this TraceTree. Should
+   *              have at least one root span (span with parentId =
+   *              Span.ROOT_SPAN_ID
+   */
+  public TraceTree(Collection<Span> spans) {
+    this.spansByParent = new SpansByParent(spans);
+    this.spansByProcessId = new SpansByProcessId(spans);
+  }
+
+  public SpansByParent getSpansByParent() {
+    return spansByParent;
+  }
+
+  public SpansByProcessId getSpansByProcessId() {
+    return spansByProcessId;
+  }
+}
diff --git a/htrace-core/src/main/java/org/htrace/Tracer.java b/htrace-core/src/main/java/org/apache/htrace/Tracer.java
similarity index 98%
rename from htrace-core/src/main/java/org/htrace/Tracer.java
rename to htrace-core/src/main/java/org/apache/htrace/Tracer.java
index ac31165..614521c 100644
--- a/htrace-core/src/main/java/org/htrace/Tracer.java
+++ b/htrace-core/src/main/java/org/apache/htrace/Tracer.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.htrace.impl.MilliSpan;
+import org.apache.htrace.impl.MilliSpan;
 
 import java.security.SecureRandom;
 import java.util.List;
diff --git a/htrace-core/src/main/java/org/htrace/impl/AlwaysSampler.java b/htrace-core/src/main/java/org/apache/htrace/impl/AlwaysSampler.java
similarity index 82%
rename from htrace-core/src/main/java/org/htrace/impl/AlwaysSampler.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/AlwaysSampler.java
index c8d53e8..7e0bf3b 100644
--- a/htrace-core/src/main/java/org/htrace/impl/AlwaysSampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/AlwaysSampler.java
@@ -14,15 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.Sampler;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
 
+/**
+ * A Sampler that always returns true.
+ */
 public final class AlwaysSampler implements Sampler<Object> {
 
-  public static final AlwaysSampler INSTANCE = new AlwaysSampler();
+  public static final AlwaysSampler INSTANCE = new AlwaysSampler(null);
 
-  private AlwaysSampler() {
+  public AlwaysSampler(HTraceConfiguration conf) {
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/impl/CountSampler.java b/htrace-core/src/main/java/org/apache/htrace/impl/CountSampler.java
similarity index 69%
rename from htrace-core/src/main/java/org/htrace/impl/CountSampler.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/CountSampler.java
index 8afcd8e..fe98916 100644
--- a/htrace-core/src/main/java/org/htrace/impl/CountSampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/CountSampler.java
@@ -14,24 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.Sampler;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
 
 import java.util.Random;
 
 /**
- * Sampler that returns true every N calls.
+ * Sampler that returns true every N calls. Specify the frequency interval by configuring a
+ * {@code long} value for {@link #SAMPLER_FREQUENCY_CONF_KEY}.
  */
 public class CountSampler implements Sampler<Object> {
+  public final static String SAMPLER_FREQUENCY_CONF_KEY = "sampler.frequency";
 
   final static Random random = new Random();
 
   final long frequency;
   long count = random.nextLong();
 
-  public CountSampler(long frequency) {
-    this.frequency = frequency;
+  public CountSampler(HTraceConfiguration conf) {
+    this.frequency = Long.parseLong(conf.get(SAMPLER_FREQUENCY_CONF_KEY), 10);
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/impl/LocalFileSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/impl/LocalFileSpanReceiver.java
similarity index 79%
rename from htrace-core/src/main/java/org/htrace/impl/LocalFileSpanReceiver.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/LocalFileSpanReceiver.java
index b05b067..7095008 100644
--- a/htrace-core/src/main/java/org/htrace/impl/LocalFileSpanReceiver.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/LocalFileSpanReceiver.java
@@ -14,20 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.htrace.HTraceConfiguration;
-import org.htrace.Span;
-import org.htrace.SpanReceiver;
-import org.mortbay.util.ajax.JSON;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
 
 import java.io.BufferedWriter;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -48,16 +45,10 @@ public class LocalFileSpanReceiver implements SpanReceiver {
   private String file;
   private FileWriter fwriter;
   private BufferedWriter bwriter;
-  private Map<String, Object> values;
   private ExecutorService executor;
   private long executorTerminationTimeoutDuration;
 
-  public LocalFileSpanReceiver() {
-  }
-
-
-  @Override
-  public void configure(HTraceConfiguration conf) {
+  public LocalFileSpanReceiver(HTraceConfiguration conf) {
     this.executorTerminationTimeoutDuration = EXECUTOR_TERMINATION_TIMEOUT_DURATION_DEFAULT;
     int capacity = conf.getInt(CAPACITY_KEY, CAPACITY_DEFAULT);
     this.file = conf.get(PATH_KEY);
@@ -72,7 +63,6 @@ public class LocalFileSpanReceiver implements SpanReceiver {
       throw new RuntimeException(ioe);
     }
     this.bwriter = new BufferedWriter(fwriter);
-    this.values = new LinkedHashMap<String, Object>();
   }
 
 
@@ -86,19 +76,9 @@ public class LocalFileSpanReceiver implements SpanReceiver {
     @Override
     public void run() {
       try {
-        values.put("TraceID", span.getTraceId());
-        values.put("SpanID", span.getSpanId());
-        values.put("ParentID", span.getParentId());
-        values.put("ProcessID", span.getProcessId());
-        values.put("Start", span.getStartTimeMillis());
-        values.put("Stop", span.getStopTimeMillis());
-        values.put("Description", span.getDescription());
-        values.put("KVAnnotations", span.getKVAnnotations());
-        values.put("TLAnnotations", span.getTimelineAnnotations());
-        bwriter.write(JSON.toString(values));
+        bwriter.write(span.toJson());
         bwriter.newLine();
         bwriter.flush();
-        values.clear();
       } catch (IOException e) {
         LOG.error("Error when writing to file: " + file, e);
       }
diff --git a/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java b/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java
new file mode 100644
index 0000000..597b566
--- /dev/null
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/MilliSpan.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace.impl;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.apache.htrace.Span;
+import org.apache.htrace.TimelineAnnotation;
+import org.apache.htrace.Tracer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * A Span implementation that stores its information in milliseconds since the
+ * epoch.
+ */
+ at JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class)
+public class MilliSpan implements Span {
+
+  private static Random rand = new Random();
+  private static ObjectWriter JSON_WRITER = new ObjectMapper().writer();
+
+  private long begin;
+  private long end;
+  private final String description;
+  private final long traceId;
+  private final long parents[];
+  private final long spanId;
+  private Map<byte[], byte[]> traceInfo = null;
+  private final String processId;
+  private List<TimelineAnnotation> timeline = null;
+
+  @Override
+  public Span child(String description) {
+    return new MilliSpan(description, traceId, spanId, rand.nextLong(), processId);
+  }
+
+  /**
+   * The public interface for constructing a MilliSpan.
+   */
+  public static class Builder {
+    private long begin;
+    private long end;
+    private String description;
+    private long traceId;
+    private long parents[];
+    private long spanId;
+    private Map<byte[], byte[]> traceInfo = null;
+    private String processId;
+    private List<TimelineAnnotation> timeline = null;
+
+    public Builder() {
+    }
+
+    public Builder begin(long begin) {
+      this.begin = begin;
+      return this;
+    }
+
+    public Builder end(long end) {
+      this.end = end;
+      return this;
+    }
+
+    public Builder description(String description) {
+      this.description = description;
+      return this;
+    }
+
+    public Builder traceId(long traceId) {
+      this.traceId = traceId;
+      return this;
+    }
+
+    public Builder parents(long parents[]) {
+      this.parents = parents;
+      return this;
+    }
+
+    public Builder parents(List<Long> parentList) {
+      long[] parents = new long[parentList.size()];
+      for (int i = 0; i < parentList.size(); i++) {
+        parents[i] = parentList.get(i).longValue();
+      }
+      this.parents = parents;
+      return this;
+    }
+
+    public Builder spanId(long spanId) {
+      this.spanId = spanId;
+      return this;
+    }
+
+    public Builder traceInfo(Map<byte[], byte[]> traceInfo) {
+      this.traceInfo = traceInfo.isEmpty() ? null : traceInfo;
+      return this;
+    }
+
+    public Builder processId(String processId) {
+      this.processId = processId;
+      return this;
+    }
+
+    public Builder timeline(List<TimelineAnnotation> timeline) {
+      this.timeline = timeline.isEmpty() ? null : timeline;
+      return this;
+    }
+
+    public MilliSpan build() {
+      return new MilliSpan(this);
+    }
+  }
+
+  private MilliSpan(Builder builder) {
+    this.begin = builder.begin;
+    this.end = builder.end;
+    this.description = builder.description;
+    this.traceId = builder.traceId;
+    this.parents = builder.parents;
+    this.spanId = builder.spanId;
+    this.traceInfo = builder.traceInfo;
+    this.processId = builder.processId;
+    this.timeline = builder.timeline;
+  }
+
+  public MilliSpan(String description, long traceId, long parentSpanId, long spanId, String processId) {
+    this.description = description;
+    this.traceId = traceId;
+    if (parentSpanId == Span.ROOT_SPAN_ID) {
+      this.parents = new long[0];
+    } else {
+      this.parents = new long[] { parentSpanId };
+    } 
+    this.spanId = spanId;
+    this.begin = System.currentTimeMillis();
+    this.end = 0;
+    this.processId = processId;
+  }
+
+  @Override
+  public synchronized void stop() {
+    if (end == 0) {
+      if (begin == 0)
+        throw new IllegalStateException("Span for " + description
+            + " has not been started");
+      end = System.currentTimeMillis();
+      Tracer.getInstance().deliver(this);
+    }
+  }
+
+  protected long currentTimeMillis() {
+    return System.currentTimeMillis();
+  }
+
+  @Override
+  public synchronized boolean isRunning() {
+    return begin != 0 && end == 0;
+  }
+
+  @Override
+  public synchronized long getAccumulatedMillis() {
+    if (begin == 0)
+      return 0;
+    if (end > 0)
+      return end - begin;
+    return currentTimeMillis() - begin;
+  }
+
+  @Override
+  public String toString() {
+    return toJson();
+  }
+
+  @Override
+  public String getDescription() {
+    return description;
+  }
+
+  @Override
+  public long getSpanId() {
+    return spanId;
+  }
+
+  // TODO: Fix API callers to deal with multiple parents, and get rid of
+  // Span.ROOT_SPAN_ID.
+  @Override
+  public long getParentId() {
+    if (parents.length == 0) {
+      return Span.ROOT_SPAN_ID;
+    }
+    return parents[0];
+  }
+
+  @Override
+  public long getTraceId() {
+    return traceId;
+  }
+
+  @Override
+  public long getStartTimeMillis() {
+    return begin;
+  }
+
+  @Override
+  public long getStopTimeMillis() {
+    return end;
+  }
+
+  @Override
+  public void addKVAnnotation(byte[] key, byte[] value) {
+    if (traceInfo == null)
+      traceInfo = new HashMap<byte[], byte[]>();
+    traceInfo.put(key, value);
+  }
+
+  @Override
+  public void addTimelineAnnotation(String msg) {
+    if (timeline == null) {
+      timeline = new ArrayList<TimelineAnnotation>();
+    }
+    timeline.add(new TimelineAnnotation(System.currentTimeMillis(), msg));
+  }
+
+  @Override
+  public Map<byte[], byte[]> getKVAnnotations() {
+    if (traceInfo == null)
+      return Collections.emptyMap();
+    return Collections.unmodifiableMap(traceInfo);
+  }
+
+  @Override
+  public List<TimelineAnnotation> getTimelineAnnotations() {
+    if (timeline == null) {
+      return Collections.emptyList();
+    }
+    return Collections.unmodifiableList(timeline);
+  }
+
+  @Override
+  public String getProcessId() {
+    return processId;
+  }
+
+  @Override
+  public String toJson() {
+    StringWriter writer = new StringWriter();
+    try {
+      JSON_WRITER.writeValue(writer, this);
+    } catch (IOException e) {
+      // An IOException should not be possible when writing to a string.
+      throw new RuntimeException(e);
+    }
+    return writer.toString();
+  }
+
+  private static long parseUnsignedHexLong(String s) {
+    return new BigInteger(s, 16).longValue();
+  }
+
+  public static class MilliSpanDeserializer
+        extends JsonDeserializer<MilliSpan> {
+    @Override
+    public MilliSpan deserialize(JsonParser jp, DeserializationContext ctxt)
+          throws IOException, JsonProcessingException {
+      JsonNode root = jp.getCodec().readTree(jp);
+      Builder builder = new Builder();
+      builder.begin(root.get("b").asLong()).
+              end(root.get("e").asLong()).
+              description(root.get("d").asText()).
+              traceId(parseUnsignedHexLong(root.get("i").asText())).
+              spanId(parseUnsignedHexLong(root.get("s").asText())).
+              processId(root.get("r").asText());
+      JsonNode parentsNode = root.get("p");
+      LinkedList<Long> parents = new LinkedList<Long>();
+      for (Iterator<JsonNode> iter = parentsNode.elements();
+           iter.hasNext(); ) {
+        JsonNode parentIdNode = iter.next();
+        parents.add(parseUnsignedHexLong(parentIdNode.asText()));
+      }
+      builder.parents(parents);
+      JsonNode traceInfoNode = root.get("n");
+      if (traceInfoNode != null) {
+        HashMap<byte[], byte[]> traceInfo = new HashMap<byte[], byte[]>();
+        for (Iterator<String> iter = traceInfoNode.fieldNames();
+             iter.hasNext(); ) {
+          String field = iter.next();
+          traceInfo.put(field.getBytes("UTF-8"),
+              traceInfoNode.get(field).asText().getBytes("UTF-8"));
+        }
+        builder.traceInfo(traceInfo);
+      }
+      JsonNode timelineNode = root.get("t");
+      if (timelineNode != null) {
+        LinkedList<TimelineAnnotation> timeline =
+            new LinkedList<TimelineAnnotation>();
+        for (Iterator<JsonNode> iter = timelineNode.elements();
+             iter.hasNext(); ) {
+          JsonNode ann = iter.next();
+          timeline.add(new TimelineAnnotation(ann.get("t").asLong(),
+              ann.get("m").asText()));
+        }
+        builder.timeline(timeline);
+      }
+      return builder.build();
+    }
+  }
+}
diff --git a/htrace-core/src/main/java/org/htrace/impl/NeverSampler.java b/htrace-core/src/main/java/org/apache/htrace/impl/NeverSampler.java
similarity index 77%
rename from htrace-core/src/main/java/org/htrace/impl/NeverSampler.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/NeverSampler.java
index 38b193a..c633da7 100644
--- a/htrace-core/src/main/java/org/htrace/impl/NeverSampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/NeverSampler.java
@@ -14,15 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.Sampler;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
 
+/**
+ * A Sampler that never returns true.
+ */
 public final class NeverSampler implements Sampler<Object> {
 
-  public static final NeverSampler INSTANCE = new NeverSampler();
+  public static final NeverSampler INSTANCE = new NeverSampler(null);
 
-  private NeverSampler() {
+  public NeverSampler(HTraceConfiguration conf) {
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/impl/POJOSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/impl/POJOSpanReceiver.java
similarity index 86%
rename from htrace-core/src/main/java/org/htrace/impl/POJOSpanReceiver.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/POJOSpanReceiver.java
index 54874e6..57e5299 100644
--- a/htrace-core/src/main/java/org/htrace/impl/POJOSpanReceiver.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/POJOSpanReceiver.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.HTraceConfiguration;
-import org.htrace.Span;
-import org.htrace.SpanReceiver;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
 
 import java.io.IOException;
 import java.util.Collection;
@@ -31,8 +31,8 @@ import java.util.HashSet;
 public class POJOSpanReceiver implements SpanReceiver {
   private final Collection<Span> spans;
 
-  @Override
-  public void configure(HTraceConfiguration conf) {
+  public POJOSpanReceiver(HTraceConfiguration conf) {
+    this.spans = new HashSet<Span>();
   }
 
   /**
@@ -42,10 +42,6 @@ public class POJOSpanReceiver implements SpanReceiver {
     return spans;
   }
 
-  public POJOSpanReceiver() {
-    this.spans = new HashSet<Span>();
-  }
-
   @Override
   public void close() throws IOException {
   }
@@ -54,5 +50,4 @@ public class POJOSpanReceiver implements SpanReceiver {
   public void receiveSpan(Span span) {
     spans.add(span);
   }
-
 }
diff --git a/htrace-core/src/main/java/org/htrace/impl/ProbabilitySampler.java b/htrace-core/src/main/java/org/apache/htrace/impl/ProbabilitySampler.java
similarity index 65%
rename from htrace-core/src/main/java/org/htrace/impl/ProbabilitySampler.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/ProbabilitySampler.java
index 1ac1f73..035a845 100644
--- a/htrace-core/src/main/java/org/htrace/impl/ProbabilitySampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/ProbabilitySampler.java
@@ -14,19 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.Sampler;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
 
 import java.util.Random;
 
+/**
+ * Sampler that returns true a certain percentage of the time. Specify the frequency interval by
+ * configuring a {@code double} value for {@link #SAMPLER_FRACTION_CONF_KEY}.
+ */
 public class ProbabilitySampler implements Sampler<Object> {
   public final double threshold;
-  private Random random;
+  private Random random = new Random();
+  public final static String SAMPLER_FRACTION_CONF_KEY = "sampler.fraction";
 
-  public ProbabilitySampler(double threshold) {
-    this.threshold = threshold;
-    random = new Random();
+  public ProbabilitySampler(HTraceConfiguration conf) {
+    this.threshold = Double.parseDouble(conf.get(SAMPLER_FRACTION_CONF_KEY));
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/impl/StandardOutSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/impl/StandardOutSpanReceiver.java
similarity index 84%
rename from htrace-core/src/main/java/org/htrace/impl/StandardOutSpanReceiver.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/StandardOutSpanReceiver.java
index f1ebada..014f32b 100644
--- a/htrace-core/src/main/java/org/htrace/impl/StandardOutSpanReceiver.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/StandardOutSpanReceiver.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.HTraceConfiguration;
-import org.htrace.Span;
-import org.htrace.SpanReceiver;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
 
 import java.io.IOException;
 
@@ -26,9 +26,7 @@ import java.io.IOException;
  * Used for testing. Simply prints to standard out any spans it receives.
  */
 public class StandardOutSpanReceiver implements SpanReceiver {
-
-  @Override
-  public void configure(HTraceConfiguration conf) {
+  public StandardOutSpanReceiver(HTraceConfiguration conf) {
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/impl/TrueIfTracingSampler.java b/htrace-core/src/main/java/org/apache/htrace/impl/TrueIfTracingSampler.java
similarity index 71%
rename from htrace-core/src/main/java/org/htrace/impl/TrueIfTracingSampler.java
rename to htrace-core/src/main/java/org/apache/htrace/impl/TrueIfTracingSampler.java
index 100650e..433a325 100644
--- a/htrace-core/src/main/java/org/htrace/impl/TrueIfTracingSampler.java
+++ b/htrace-core/src/main/java/org/apache/htrace/impl/TrueIfTracingSampler.java
@@ -14,19 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
-import org.htrace.Sampler;
-import org.htrace.Trace;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
+import org.apache.htrace.Trace;
 
 /**
- * A Sampler that returns true if and only if tracing is on the current thread.
+ * A Sampler that returns true if and only if tracing is enabled for the current thread.
  */
 public class TrueIfTracingSampler implements Sampler<Object> {
 
-  public static final TrueIfTracingSampler INSTANCE = new TrueIfTracingSampler();
+  public static final TrueIfTracingSampler INSTANCE =
+    new TrueIfTracingSampler(null);
 
-  private TrueIfTracingSampler() {
+  public TrueIfTracingSampler(HTraceConfiguration conf) {
   }
 
   @Override
diff --git a/htrace-core/src/main/java/org/htrace/wrappers/TraceCallable.java b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceCallable.java
similarity index 93%
rename from htrace-core/src/main/java/org/htrace/wrappers/TraceCallable.java
rename to htrace-core/src/main/java/org/apache/htrace/wrappers/TraceCallable.java
index 5b25893..e761fbf 100644
--- a/htrace-core/src/main/java/org/htrace/wrappers/TraceCallable.java
+++ b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceCallable.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.wrappers;
+package org.apache.htrace.wrappers;
 
-import org.htrace.Span;
-import org.htrace.Trace;
-import org.htrace.TraceScope;
+import org.apache.htrace.Span;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceScope;
 
 import java.util.concurrent.Callable;
 
diff --git a/htrace-core/src/main/java/org/htrace/wrappers/TraceExecutorService.java b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceExecutorService.java
similarity index 98%
rename from htrace-core/src/main/java/org/htrace/wrappers/TraceExecutorService.java
rename to htrace-core/src/main/java/org/apache/htrace/wrappers/TraceExecutorService.java
index 95ae88a..03e891f 100644
--- a/htrace-core/src/main/java/org/htrace/wrappers/TraceExecutorService.java
+++ b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceExecutorService.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.wrappers;
+package org.apache.htrace.wrappers;
 
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/htrace-core/src/main/java/org/htrace/wrappers/TraceProxy.java b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceProxy.java
similarity index 93%
rename from htrace-core/src/main/java/org/htrace/wrappers/TraceProxy.java
rename to htrace-core/src/main/java/org/apache/htrace/wrappers/TraceProxy.java
index 42cdd5a..c06befd 100644
--- a/htrace-core/src/main/java/org/htrace/wrappers/TraceProxy.java
+++ b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceProxy.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.wrappers;
+package org.apache.htrace.wrappers;
 
-import org.htrace.Sampler;
-import org.htrace.Trace;
-import org.htrace.TraceScope;
+import org.apache.htrace.Sampler;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceScope;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
diff --git a/htrace-core/src/main/java/org/htrace/wrappers/TraceRunnable.java b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceRunnable.java
similarity index 90%
rename from htrace-core/src/main/java/org/htrace/wrappers/TraceRunnable.java
rename to htrace-core/src/main/java/org/apache/htrace/wrappers/TraceRunnable.java
index 9f7ea1e..6d370c8 100644
--- a/htrace-core/src/main/java/org/htrace/wrappers/TraceRunnable.java
+++ b/htrace-core/src/main/java/org/apache/htrace/wrappers/TraceRunnable.java
@@ -14,11 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.wrappers;
+package org.apache.htrace.wrappers;
 
-import org.htrace.Span;
-import org.htrace.Trace;
-import org.htrace.TraceScope;
+import org.apache.htrace.Span;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceScope;
 
 /**
  * Wrap a Runnable with a Span that survives a change in threads.
@@ -61,4 +61,8 @@ public class TraceRunnable implements Runnable {
   private String getDescription() {
     return this.description == null ? Thread.currentThread().getName() : description;
   }
+
+  public Runnable getRunnable() {
+    return runnable;
+  }
 }
diff --git a/htrace-core/src/main/java/org/htrace/TraceTree.java b/htrace-core/src/main/java/org/htrace/TraceTree.java
deleted file mode 100644
index fadcf2f..0000000
--- a/htrace-core/src/main/java/org/htrace/TraceTree.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.htrace;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.util.Collection;
-
-/**
- * Used to create the graph formed by spans.
- */
-public class TraceTree {
-  public static final Log LOG = LogFactory.getLog(Tracer.class);
-  private Multimap<Long, Span> spansByParentID;
-  private Collection<Span> spans;
-  private Multimap<String, Span> spansByPid;
-
-  /**
-   * Create a new TraceTree
-   *
-   * @param spans The collection of spans to use to create this TraceTree. Should
-   *              have at least one root span (span with parentId =
-   *              Span.ROOT_SPAN_ID
-   */
-  public TraceTree(Collection<Span> spans) {
-    this.spans = ImmutableList.copyOf(spans);
-    this.spansByParentID = HashMultimap.<Long, Span>create();
-    this.spansByPid = HashMultimap.<String, Span>create();
-
-    for (Span s : this.spans) {
-      if (s.getProcessId() != null) {
-        spansByPid.put(s.getProcessId(), s);
-      } else {
-        LOG.warn("Encountered span with null processId. This should not happen. Span: "
-            + s);
-      }
-      spansByParentID.put(s.getParentId(), s);
-    }
-  }
-
-  /**
-   * @return The collection of spans given to this TraceTree at construction.
-   */
-  public Collection<Span> getSpans() {
-    return spans;
-  }
-
-  /**
-   * @return A copy of the MultiMap from parent span ID -> children of span with
-   *         that ID.
-   */
-  public Multimap<Long, Span> getSpansByParentIdMap() {
-    return HashMultimap.<Long, Span>create(spansByParentID);
-  }
-
-  /**
-   * @return A collection of the root spans (spans with parent ID =
-   *         Span.ROOT_SPAN_ID) in this tree.
-   */
-  public Collection<Span> getRoots() {
-    Collection<Span> roots = spansByParentID.get(Span.ROOT_SPAN_ID);
-    if (roots != null) {
-      return roots;
-    }
-    throw new IllegalStateException(
-        "TraceTree is not correctly formed - there are no root spans in the collection provided at construction.");
-  }
-
-  /**
-   * @return A copy of the Multimap from String process ID -> spans with that
-   *         process ID. If process ID was not set in Trace.java, all spans will
-   *         have empty string process IDs.
-   */
-  public Multimap<String, Span> getSpansByPidMap() {
-    return HashMultimap.<String, Span>create(spansByPid);
-  }
-}
\ No newline at end of file
diff --git a/htrace-core/src/main/java/org/htrace/impl/MilliSpan.java b/htrace-core/src/main/java/org/htrace/impl/MilliSpan.java
deleted file mode 100644
index 39c9671..0000000
--- a/htrace-core/src/main/java/org/htrace/impl/MilliSpan.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.htrace.impl;
-
-import org.htrace.Span;
-import org.htrace.TimelineAnnotation;
-import org.htrace.Tracer;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
-/**
- * A Span implementation that stores its information in milliseconds since the
- * epoch.
- */
-public class MilliSpan implements Span {
-
-  private static Random rand = new Random();
-
-  private long start;
-  private long stop;
-  private final String description;
-  private final long traceId;
-  private final long parentSpanId;
-  private final long spanId;
-  private Map<byte[], byte[]> traceInfo = null;
-  private final String processId;
-  private List<TimelineAnnotation> timeline = null;
-
-  @Override
-  public Span child(String description) {
-    return new MilliSpan(description, traceId, spanId, rand.nextLong(), processId);
-  }
-
-  public MilliSpan(String description, long traceId, long parentSpanId, long spanId, String processId) {
-    this.description = description;
-    this.traceId = traceId;
-    this.parentSpanId = parentSpanId;
-    this.spanId = spanId;
-    this.start = System.currentTimeMillis();
-    this.stop = 0;
-    this.processId = processId;
-  }
-
-  @Override
-  public synchronized void stop() {
-    if (stop == 0) {
-      if (start == 0)
-        throw new IllegalStateException("Span for " + description
-            + " has not been started");
-      stop = System.currentTimeMillis();
-      Tracer.getInstance().deliver(this);
-    }
-  }
-
-  protected long currentTimeMillis() {
-    return System.currentTimeMillis();
-  }
-
-  @Override
-  public synchronized boolean isRunning() {
-    return start != 0 && stop == 0;
-  }
-
-  @Override
-  public synchronized long getAccumulatedMillis() {
-    if (start == 0)
-      return 0;
-    if (stop > 0)
-      return stop - start;
-    return currentTimeMillis() - start;
-  }
-
-  @Override
-  public String toString() {
-    return String.format("Span{Id:0x%16x,parentId:0x%16x,desc:%s}", spanId, parentSpanId, description);
-  }
-
-  @Override
-  public String getDescription() {
-    return description;
-  }
-
-  @Override
-  public long getSpanId() {
-    return spanId;
-  }
-
-  @Override
-  public long getParentId() {
-    return parentSpanId;
-  }
-
-  @Override
-  public long getTraceId() {
-    return traceId;
-  }
-
-  @Override
-  public long getStartTimeMillis() {
-    return start;
-  }
-
-  @Override
-  public long getStopTimeMillis() {
-    return stop;
-  }
-
-  @Override
-  public void addKVAnnotation(byte[] key, byte[] value) {
-    if (traceInfo == null)
-      traceInfo = new HashMap<byte[], byte[]>();
-    traceInfo.put(key, value);
-  }
-
-  @Override
-  public void addTimelineAnnotation(String msg) {
-    if (timeline == null) {
-      timeline = new ArrayList<TimelineAnnotation>();
-    }
-    timeline.add(new TimelineAnnotation(System.currentTimeMillis(), msg));
-  }
-
-  @Override
-  public Map<byte[], byte[]> getKVAnnotations() {
-    if (traceInfo == null)
-      return Collections.emptyMap();
-    return Collections.unmodifiableMap(traceInfo);
-  }
-
-  @Override
-  public List<TimelineAnnotation> getTimelineAnnotations() {
-    if (timeline == null) {
-      return Collections.emptyList();
-    }
-    return Collections.unmodifiableList(timeline);
-  }
-
-  @Override
-  public String getProcessId() {
-    return processId;
-  }
-}
diff --git a/htrace-core/src/test/java/org/htrace/TestCountSampler.java b/htrace-core/src/test/java/org/apache/htrace/TestCountSampler.java
similarity index 77%
rename from htrace-core/src/test/java/org/htrace/TestCountSampler.java
rename to htrace-core/src/test/java/org/apache/htrace/TestCountSampler.java
index 25a0d9e..1cc7753 100644
--- a/htrace-core/src/test/java/org/htrace/TestCountSampler.java
+++ b/htrace-core/src/test/java/org/apache/htrace/TestCountSampler.java
@@ -14,9 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
-import org.htrace.impl.CountSampler;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.impl.CountSampler;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -24,8 +25,10 @@ public class TestCountSampler {
 
   @Test
   public void testNext() {
-    CountSampler half = new CountSampler(2);
-    CountSampler hundred = new CountSampler(100);
+    CountSampler half = new CountSampler(HTraceConfiguration.
+        fromKeyValuePairs("sampler.frequency", "2"));
+    CountSampler hundred = new CountSampler(HTraceConfiguration.
+        fromKeyValuePairs("sampler.frequency", "100"));
     int halfCount = 0;
     int hundredCount = 0;
     for (int i = 0; i < 200; i++) {
diff --git a/htrace-core/src/test/java/org/htrace/TestHTrace.java b/htrace-core/src/test/java/org/apache/htrace/TestHTrace.java
similarity index 67%
rename from htrace-core/src/test/java/org/htrace/TestHTrace.java
rename to htrace-core/src/test/java/org/apache/htrace/TestHTrace.java
index f613755..7b058d7 100644
--- a/htrace-core/src/test/java/org/htrace/TestHTrace.java
+++ b/htrace-core/src/test/java/org/apache/htrace/TestHTrace.java
@@ -14,12 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
-
-import com.google.common.collect.Multimap;
-import org.htrace.impl.LocalFileSpanReceiver;
-import org.htrace.impl.POJOSpanReceiver;
-import org.htrace.impl.StandardOutSpanReceiver;
+package org.apache.htrace;
+
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.TraceTree;
+import org.apache.htrace.TraceTree.SpansByParent;
+import org.apache.htrace.impl.LocalFileSpanReceiver;
+import org.apache.htrace.impl.POJOSpanReceiver;
+import org.apache.htrace.impl.StandardOutSpanReceiver;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -56,14 +60,14 @@ public class TestHTrace {
       }
       HashMap<String, String> conf = new HashMap<String, String>();
       conf.put("local-file-span-receiver.path", fileName);
-      LocalFileSpanReceiver receiver = new LocalFileSpanReceiver();
-      receiver.configure(HTraceConfiguration.fromMap(conf));
+      LocalFileSpanReceiver receiver =
+          new LocalFileSpanReceiver(HTraceConfiguration.fromMap(conf));
       rcvrs.add(receiver);
     } else {
-      rcvrs.add(new StandardOutSpanReceiver());
+      rcvrs.add(new StandardOutSpanReceiver(HTraceConfiguration.EMPTY));
     }
 
-    POJOSpanReceiver psr = new POJOSpanReceiver();
+    POJOSpanReceiver psr = new POJOSpanReceiver(HTraceConfiguration.EMPTY);
     rcvrs.add(psr);
     runTraceCreatorTraces(new TraceCreator(rcvrs));
 
@@ -73,7 +77,8 @@ public class TestHTrace {
 
     Collection<Span> spans = psr.getSpans();
     TraceTree traceTree = new TraceTree(spans);
-    Collection<Span> roots = traceTree.getRoots();
+    Collection<Span> roots = traceTree.getSpansByParent().find(Span.ROOT_SPAN_ID);
+    Assert.assertTrue("Trace tree must have roots", !roots.isEmpty());
     Assert.assertEquals(numTraces, roots.size());
 
     Map<String, Span> descriptionToRootSpan = new HashMap<String, Span>();
@@ -88,21 +93,21 @@ public class TestHTrace {
     Assert.assertTrue(descriptionToRootSpan.keySet().contains(
         TraceCreator.THREADED_TRACE_ROOT));
 
-    Multimap<Long, Span> spansByParentId = traceTree.getSpansByParentIdMap();
+    SpansByParent spansByParentId = traceTree.getSpansByParent();
     Span rpcTraceRoot = descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT);
-    Assert.assertEquals(1, spansByParentId.get(rpcTraceRoot.getSpanId()).size());
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceRoot.getSpanId()).size());
 
-    Span rpcTraceChild1 = spansByParentId.get(rpcTraceRoot.getSpanId())
+    Span rpcTraceChild1 = spansByParentId.find(rpcTraceRoot.getSpanId())
         .iterator().next();
-    Assert.assertEquals(1, spansByParentId.get(rpcTraceChild1.getSpanId()).size());
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild1.getSpanId()).size());
 
-    Span rpcTraceChild2 = spansByParentId.get(rpcTraceChild1.getSpanId())
+    Span rpcTraceChild2 = spansByParentId.find(rpcTraceChild1.getSpanId())
         .iterator().next();
-    Assert.assertEquals(1, spansByParentId.get(rpcTraceChild2.getSpanId()).size());
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild2.getSpanId()).size());
 
-    Span rpcTraceChild3 = spansByParentId.get(rpcTraceChild2.getSpanId())
+    Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId())
         .iterator().next();
-    Assert.assertEquals(0, spansByParentId.get(rpcTraceChild3.getSpanId()).size());
+    Assert.assertEquals(0, spansByParentId.find(rpcTraceChild3.getSpanId()).size());
   }
 
   private void runTraceCreatorTraces(TraceCreator tc) {
diff --git a/htrace-core/src/test/java/org/htrace/TestHTraceConfiguration.java b/htrace-core/src/test/java/org/apache/htrace/TestHTraceConfiguration.java
similarity index 96%
rename from htrace-core/src/test/java/org/htrace/TestHTraceConfiguration.java
rename to htrace-core/src/test/java/org/apache/htrace/TestHTraceConfiguration.java
index 5506ddc..440a826 100644
--- a/htrace-core/src/test/java/org/htrace/TestHTraceConfiguration.java
+++ b/htrace-core/src/test/java/org/apache/htrace/TestHTraceConfiguration.java
@@ -15,11 +15,12 @@
  * limitations under the License.
  */
 
-package org.htrace;
+package org.apache.htrace;
 
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.htrace.HTraceConfiguration;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
diff --git a/htrace-core/src/test/java/org/htrace/TestSampler.java b/htrace-core/src/test/java/org/apache/htrace/TestSampler.java
similarity index 60%
rename from htrace-core/src/test/java/org/htrace/TestSampler.java
rename to htrace-core/src/test/java/org/apache/htrace/TestSampler.java
index 9bb2e8b..31060d4 100644
--- a/htrace-core/src/test/java/org/htrace/TestSampler.java
+++ b/htrace-core/src/test/java/org/apache/htrace/TestSampler.java
@@ -14,13 +14,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.htrace.Sampler;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceInfo;
+import org.apache.htrace.TraceScope;
+import org.apache.htrace.impl.AlwaysSampler;
+import org.apache.htrace.impl.NeverSampler;
 import org.junit.Assert;
 import org.junit.Test;
 
 public class TestSampler {
   @Test
+  public void testSamplerBuilder() {
+    Sampler alwaysSampler = new SamplerBuilder(
+        HTraceConfiguration.fromKeyValuePairs("sampler", "AlwaysSampler")).
+        build();
+    Assert.assertEquals(AlwaysSampler.class, alwaysSampler.getClass());
+
+    Sampler neverSampler = new SamplerBuilder(
+        HTraceConfiguration.fromKeyValuePairs("sampler", "NeverSampler")).
+        build();
+    Assert.assertEquals(NeverSampler.class, neverSampler.getClass());
+
+    Sampler neverSampler2 = new SamplerBuilder(HTraceConfiguration.
+        fromKeyValuePairs("sampler", "NonExistentSampler")).
+        build();
+    Assert.assertEquals(NeverSampler.class, neverSampler2.getClass());
+  }
+
+  @Test
   public void testParamterizedSampler() {
     TestParamSampler sampler = new TestParamSampler();
     TraceScope s = Trace.startSpan("test", sampler, 1);
diff --git a/htrace-core/src/test/java/org/apache/htrace/TestSpanReceiverBuilder.java b/htrace-core/src/test/java/org/apache/htrace/TestSpanReceiverBuilder.java
new file mode 100644
index 0000000..16536af
--- /dev/null
+++ b/htrace-core/src/test/java/org/apache/htrace/TestSpanReceiverBuilder.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace;
+
+import org.apache.htrace.impl.LocalFileSpanReceiver;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestSpanReceiverBuilder {
+  /**
+   * Test that if no span receiver is configured, the builder returns null.
+   */
+  @Test
+  public void testGetNullSpanReceiver() {
+    SpanReceiverBuilder builder =
+        new SpanReceiverBuilder(HTraceConfiguration.EMPTY).logErrors(false);
+    SpanReceiver rcvr = builder.build();
+    Assert.assertEquals(null, rcvr);
+  }
+
+  private static SpanReceiver createSpanReceiver(Map<String, String> m) {
+    HTraceConfiguration hconf = HTraceConfiguration.fromMap(m);
+    SpanReceiverBuilder builder =
+        new SpanReceiverBuilder(hconf).
+            logErrors(false);
+    return builder.build();
+  }
+
+  /**
+   * Test getting various SpanReceiver objects.
+   */
+  @Test
+  public void testGetSpanReceivers() throws Exception {
+    HashMap<String, String> confMap = new HashMap<String, String>();
+
+    // Create LocalFileSpanReceiver
+    confMap.put(LocalFileSpanReceiver.PATH_KEY, "/tmp/foo");
+    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
+        "org.apache.htrace.impl.LocalFileSpanReceiver");
+    SpanReceiver rcvr = createSpanReceiver(confMap);
+    Assert.assertEquals("org.apache.htrace.impl.LocalFileSpanReceiver",
+        rcvr.getClass().getName());
+    rcvr.close();
+
+    // Create POJOSpanReceiver
+    confMap.remove(LocalFileSpanReceiver.PATH_KEY);
+    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY, "POJOSpanReceiver");
+    rcvr = createSpanReceiver(confMap);
+    Assert.assertEquals("org.apache.htrace.impl.POJOSpanReceiver",
+        rcvr.getClass().getName());
+    rcvr.close();
+
+    // Create StandardOutSpanReceiver
+    confMap.remove(LocalFileSpanReceiver.PATH_KEY);
+    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
+        "org.apache.htrace.impl.StandardOutSpanReceiver");
+    rcvr = createSpanReceiver(confMap);
+    Assert.assertEquals("org.apache.htrace.impl.StandardOutSpanReceiver",
+        rcvr.getClass().getName());
+    rcvr.close();
+  }
+
+  public static class TestSpanReceiver implements SpanReceiver {
+    final static String SUCCEEDS = "test.span.receiver.succeeds";
+
+    public TestSpanReceiver(HTraceConfiguration conf) {
+      if (conf.get(SUCCEEDS) == null) {
+        throw new RuntimeException("Can't create TestSpanReceiver: " +
+            "invalid configuration.");
+      }
+    }
+
+    @Override
+    public void receiveSpan(Span span) {
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+  }
+
+  /**
+   * Test trying to create a SpanReceiver that experiences an error in the
+   * constructor.
+   */
+  @Test
+  public void testGetSpanReceiverWithConstructorError() throws Exception {
+    HashMap<String, String> confMap = new HashMap<String, String>();
+
+    // Create TestSpanReceiver
+    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
+        TestSpanReceiver.class.getName());
+    confMap.put(TestSpanReceiver.SUCCEEDS, "true");
+    SpanReceiver rcvr = createSpanReceiver(confMap);
+    Assert.assertEquals(TestSpanReceiver.class.getName(),
+        rcvr.getClass().getName());
+    rcvr.close();
+
+    // Fail to create TestSpanReceiver
+    confMap.remove(TestSpanReceiver.SUCCEEDS);
+    rcvr = createSpanReceiver(confMap);
+    Assert.assertEquals(null, rcvr);
+  }
+}
diff --git a/htrace-core/src/test/java/org/htrace/TraceCreator.java b/htrace-core/src/test/java/org/apache/htrace/TraceCreator.java
similarity index 95%
rename from htrace-core/src/test/java/org/htrace/TraceCreator.java
rename to htrace-core/src/test/java/org/apache/htrace/TraceCreator.java
index 7a034b8..f577656 100644
--- a/htrace-core/src/test/java/org/htrace/TraceCreator.java
+++ b/htrace-core/src/test/java/org/apache/htrace/TraceCreator.java
@@ -14,11 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace;
+package org.apache.htrace;
 
 import java.util.Collection;
 import java.util.Random;
 
+import org.apache.htrace.Sampler;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceInfo;
+import org.apache.htrace.TraceScope;
+
 /**
  * Does some stuff and traces it.
  */
diff --git a/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java
new file mode 100644
index 0000000..677ec61
--- /dev/null
+++ b/htrace-core/src/test/java/org/apache/htrace/impl/TestMilliSpan.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace.impl;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.htrace.Span;
+import org.apache.htrace.TimelineAnnotation;
+import org.junit.Test;
+
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+public class TestMilliSpan {
+  private void compareSpans(Span expected, Span got) throws Exception {
+    assertEquals(expected.getStartTimeMillis(), got.getStartTimeMillis());
+    assertEquals(expected.getStopTimeMillis(), got.getStopTimeMillis());
+    assertEquals(expected.getDescription(), got.getDescription());
+    assertEquals(expected.getTraceId(), got.getTraceId());
+    assertEquals(expected.getSpanId(), got.getSpanId());
+    assertEquals(expected.getProcessId(), got.getProcessId());
+    assertEquals(expected.getParentId(), got.getParentId());
+    Map<byte[], byte[]> expectedT = expected.getKVAnnotations();
+    Map<byte[], byte[]> gotT = got.getKVAnnotations();
+    if (expectedT == null) {
+      assertEquals(null, gotT);
+    } else {
+      assertEquals(expectedT.size(), gotT.size());
+      Map<String, String> expectedTMap = new HashMap<String, String>();
+      for (byte[] key : expectedT.keySet()) {
+        expectedTMap.put(new String(key, "UTF-8"),
+            new String(expectedT.get(key), "UTF-8"));
+      }
+      Map<String, String> gotTMap = new HashMap<String, String>();
+      for (byte[] key : gotT.keySet()) {
+        gotTMap.put(new String(key, "UTF-8"),
+            new String(gotT.get(key), "UTF-8"));
+      }
+      for (String key : expectedTMap.keySet()) {
+        assertEquals(expectedTMap.get(key), gotTMap.get(key));
+      }
+    }
+    List<TimelineAnnotation> expectedTimeline =
+        expected.getTimelineAnnotations();
+    List<TimelineAnnotation> gotTimeline =
+        got.getTimelineAnnotations();
+    if (expectedTimeline == null) {
+      assertEquals(null, gotTimeline);
+    } else {
+      assertEquals(expectedTimeline.size(), gotTimeline.size());
+      Iterator<TimelineAnnotation> iter = gotTimeline.iterator();
+      for (TimelineAnnotation expectedAnn : expectedTimeline) {
+        TimelineAnnotation gotAnn =  iter.next();
+        assertEquals(expectedAnn.getMessage(), gotAnn.getMessage());
+        assertEquals(expectedAnn.getTime(), gotAnn.getTime());
+      }
+    }
+  }
+
+  @Test
+  public void testJsonSerialization() throws Exception {
+    MilliSpan span = new MilliSpan.Builder().
+        description("foospan").
+        begin(123L).
+        end(456L).
+        parents(new long[] { 7L }).
+        processId("b2404.halxg.com:8080").
+        spanId(989L).
+        traceId(444).build();
+    String json = span.toJson();
+    ObjectMapper mapper = new ObjectMapper();
+    MilliSpan dspan = mapper.readValue(json, MilliSpan.class);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithNegativeLongValue() throws Exception {
+    MilliSpan span = new MilliSpan.Builder().
+        description("foospan").
+        begin(-1L).
+        end(-1L).
+        parents(new long[] { -1L }).
+        processId("b2404.halxg.com:8080").
+        spanId(-1L).
+        traceId(-1L).build();
+    String json = span.toJson();
+    ObjectMapper mapper = new ObjectMapper();
+    MilliSpan dspan = mapper.readValue(json, MilliSpan.class);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithRandomLongValue() throws Exception {
+    Random random = new SecureRandom();
+    MilliSpan span = new MilliSpan.Builder().
+        description("foospan").
+        begin(random.nextLong()).
+        end(random.nextLong()).
+        parents(new long[] { random.nextLong() }).
+        processId("b2404.halxg.com:8080").
+        spanId(random.nextLong()).
+        traceId(random.nextLong()).build();
+    String json = span.toJson();
+    ObjectMapper mapper = new ObjectMapper();
+    MilliSpan dspan = mapper.readValue(json, MilliSpan.class);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithOptionalFields() throws Exception {
+    MilliSpan.Builder builder = new MilliSpan.Builder().
+        description("foospan").
+        begin(300).
+        end(400).
+        parents(new long[] { }).
+        processId("b2408.halxg.com:8080").
+        spanId(111111111L).
+        traceId(4443);
+    Map<byte[], byte[]> traceInfo = new HashMap<byte[], byte[]>();
+    traceInfo.put("abc".getBytes("UTF-8"), "123".getBytes("UTF-8"));
+    traceInfo.put("def".getBytes("UTF-8"), "456".getBytes("UTF-8"));
+    builder.traceInfo(traceInfo);
+    List<TimelineAnnotation> timeline = new LinkedList<TimelineAnnotation>();
+    timeline.add(new TimelineAnnotation(310L, "something happened"));
+    timeline.add(new TimelineAnnotation(380L, "something else happened"));
+    timeline.add(new TimelineAnnotation(390L, "more things"));
+    builder.timeline(timeline);
+    MilliSpan span = builder.build();
+    String json = span.toJson();
+    ObjectMapper mapper = new ObjectMapper();
+    MilliSpan dspan = mapper.readValue(json, MilliSpan.class);
+    compareSpans(span, dspan);
+  }
+}
diff --git a/htrace-core/src/web/index.html b/htrace-core/src/web/index.html
new file mode 100644
index 0000000..cc73492
--- /dev/null
+++ b/htrace-core/src/web/index.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<head><title>HTRACE</title></head>
+Hello, world.<p/>
+</body>
+</html>
diff --git a/htrace-core/src/web/nested/nested.html b/htrace-core/src/web/nested/nested.html
new file mode 100644
index 0000000..18fa410
--- /dev/null
+++ b/htrace-core/src/web/nested/nested.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<head><title>HTRACE</title></head>
+Nested world.<p/>
+</body>
+</html>
diff --git a/htrace-flume/README.md b/htrace-flume/README.md
new file mode 100644
index 0000000..9181fc8
--- /dev/null
+++ b/htrace-flume/README.md
@@ -0,0 +1,76 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+htrace-flume
+============
+
+htrace-flume provides the span receiver which sends tracing spans to Flume collector.
+
+Tutorial
+--------
+
+1) build and deploy
+
+	$ cd htrace/htrace-flume
+	$ mvn compile assembly:single
+	$ cp target/htrace-flume-*-jar-with-dependencies.jar $HADOOP_HOME/share/hadoop/hdfs/lib/
+
+2) Edit hdfs-site.xml to include the following:
+
+	<property>
+		<name>hadoop.trace.spanreceiver.classes</name>
+		<value>org.htrace.impl.FlumeSpanReceiver</value>
+	</property>
+	<property>
+		<name>hadoop.htrace.flume.hostname</name>
+		<value>127.0.0.1</value>
+	</property>
+	<property>
+		<name>hadoop.htrace.flume.port</name>
+		<value>60000</value>
+	</property>
+
+3) Setup flume collector
+
+Create flume-conf.properties file. Below is a sample that sets up an hdfs sink.
+
+	agent.sources = avro-collection-source
+	agent.channels = memoryChannel
+	agent.sinks = loggerSink hdfs-sink
+
+	# avro source - should match the configurations in hdfs-site.xml
+	agent.sources.avro-collection-source.type = avro
+	agent.sources.avro-collection-source.bind = 127.0.0.1
+	agent.sources.avro-collection-source.port = 60000
+	agent.sources.avro-collection-source.channels = memoryChannel
+
+	#sample hdfs-sink, change to any sink that flume supports
+	agent.sinks.hdfs-sink.type = hdfs
+	agent.sinks.hdfs-sink.hdfs.path = hdfs://127.0.0.1:9000/flume
+	agent.sinks.hdfs-sink.channel = memoryChannel
+	agent.sinks.hdfs-sink.hdfs.fileType = DataStream
+	agent.sinks.hdfs-sink.hdfs.writeFormat = Text
+	agent.sinks.hdfs-sink.hdfs.rollSize = 0
+	agent.sinks.hdfs-sink.hdfs.rollCount = 10000
+	agent.sinks.hdfs-sink.hdfs.batchSize = 100
+
+	# memory channel
+	agent.channels.memoryChannel.capacity = 10000
+	agent.channels.memoryChannel.transactionCapacity = 1000
+
+Run flume agent using command "flume-ng agent -c ./conf/ -f ./conf/flume-conf.properties -n agent"
+
diff --git a/htrace-zipkin/pom.xml b/htrace-flume/pom.xml
similarity index 70%
copy from htrace-zipkin/pom.xml
copy to htrace-flume/pom.xml
index d9f8116..cb03419 100644
--- a/htrace-zipkin/pom.xml
+++ b/htrace-flume/pom.xml
@@ -12,25 +12,35 @@ language governing permissions and limitations under the License. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <artifactId>htrace-zipkin</artifactId>
+  <artifactId>htrace-flume</artifactId>
   <packaging>jar</packaging>
 
   <parent>
     <artifactId>htrace</artifactId>
-    <groupId>org.htrace</groupId>
-    <version>3.0.4</version>
+    <groupId>org.apache.htrace</groupId>
+    <version>3.1.0-incubating</version>
+    <relativePath>..</relativePath>
   </parent>
 
-  <name>htrace-zipkin</name>
-  <url>https://github.com/cloudera/htrace</url>
+  <name>htrace-flume</name>
+  <description>A 'SpanReceiver' implementation that sends spans to Flume collector.</description>
+  <url>http://incubator.apache.org/projects/htrace.html</url>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <flume.version>1.5.2</flume.version>
   </properties>
 
   <build>
     <plugins>
       <plugin>
+        <!--Make it so assembly:single does nothing in here-->
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <skipAssembly>true</skipAssembly>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
       </plugin>
@@ -66,11 +76,18 @@ language governing permissions and limitations under the License. -->
   <dependencies>
     <!-- Module deps. -->
     <dependency>
-      <groupId>org.htrace</groupId>
+      <groupId>org.apache.htrace</groupId>
       <artifactId>htrace-core</artifactId>
       <version>${project.version}</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.htrace</groupId>
+      <artifactId>htrace-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
     <!-- Global deps. -->
     <dependency>
       <groupId>commons-logging</groupId>
@@ -78,25 +95,22 @@ language governing permissions and limitations under the License. -->
       <scope>provided</scope>
     </dependency>
     <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>
     </dependency>
-    <!-- Zipkin specific deps. -->
+    <!-- Flume specific deps. -->
     <dependency>
-      <groupId>org.apache.thrift</groupId>
-      <artifactId>libthrift</artifactId>
-      <version>0.9.0</version>
+      <groupId>org.apache.flume</groupId>
+      <artifactId>flume-ng-sdk</artifactId>
+      <version>${flume.version}</version>
     </dependency>
     <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>1.7</version>
+      <groupId>org.apache.flume</groupId>
+      <artifactId>flume-ng-sdk</artifactId>
+      <version>${flume.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
     </dependency>
   </dependencies>
 </project>
diff --git a/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
new file mode 100644
index 0000000..54b8a14
--- /dev/null
+++ b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.impl;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.flume.Event;
+import org.apache.flume.FlumeException;
+import org.apache.flume.api.RpcClient;
+import org.apache.flume.api.RpcClientFactory;
+import org.apache.flume.event.EventBuilder;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+
+public class FlumeSpanReceiver implements SpanReceiver {
+  private static final Log LOG = LogFactory.getLog(FlumeSpanReceiver.class);
+
+  public static final String NUM_THREADS_KEY = "htrace.flume.num-threads";
+  public static final int DEFAULT_NUM_THREADS = 1;
+  public static final String FLUME_HOSTNAME_KEY = "htrace.flume.hostname";
+  public static final String DEFAULT_FLUME_HOSTNAME = "localhost";
+  public static final String FLUME_PORT_KEY = "htrace.flume.port";
+  public static final String FLUME_BATCHSIZE_KEY = "htrace.flume.batchsize";
+  public static final int DEFAULT_FLUME_BATCHSIZE = 100;
+  
+  /**
+   * How long this receiver will try and wait for all threads to shutdown.
+   */
+  private static final int SHUTDOWN_TIMEOUT = 30;
+
+  /**
+   * How many errors in a row before we start dropping traces on the floor.
+   */
+  private static final int MAX_ERRORS = 10;
+
+  /**
+   * The queue that will get all HTrace spans that are to be sent.
+   */
+  private final BlockingQueue<Span> queue;
+
+  /**
+   * Boolean used to signal that the threads should end.
+   */
+  private final AtomicBoolean running = new AtomicBoolean(true);
+
+  /**
+   * The thread factory used to create new ExecutorService.
+   * <p/>
+   * This will be the same factory for the lifetime of this object so that
+   * no thread names will ever be duplicated.
+   */
+  private final ThreadFactory tf;
+
+  ////////////////////
+  /// Variables that will change on each call to configure()
+  ///////////////////
+  private ExecutorService service;
+  private int maxSpanBatchSize;
+  private String flumeHostName;
+  private int flumePort;
+
+  public FlumeSpanReceiver(HTraceConfiguration conf) {
+    this.queue = new ArrayBlockingQueue<Span>(1000);
+    this.tf = new SimpleThreadFactory();
+    configure(conf);
+  }
+
+  private class SimpleThreadFactory implements ThreadFactory {
+    final AtomicLong count = new AtomicLong(0);
+    @Override
+    public Thread newThread(Runnable arg0) {
+      String name = String.format("flumeSpanReceiver-%d", count.getAndIncrement());
+      Thread t = new Thread(arg0, name);
+      t.setDaemon(true);
+      return t;
+    }
+  }
+
+  private void configure (HTraceConfiguration conf) {
+
+    // Read configuration
+    int numThreads = conf.getInt(NUM_THREADS_KEY, DEFAULT_NUM_THREADS);
+    this.flumeHostName = conf.get(FLUME_HOSTNAME_KEY, DEFAULT_FLUME_HOSTNAME);
+    this.flumePort = conf.getInt(FLUME_PORT_KEY, 0);
+    if (this.flumePort == 0) {
+      throw new IllegalArgumentException(FLUME_PORT_KEY + " is required in configuration.");
+    }
+    this.maxSpanBatchSize = conf.getInt(FLUME_BATCHSIZE_KEY, DEFAULT_FLUME_BATCHSIZE);
+
+    // Initialize executors
+    // If there are already threads running tear them down.
+    if (this.service != null) {
+      this.service.shutdownNow();
+      this.service = null;
+    }
+    this.service = Executors.newFixedThreadPool(numThreads, tf);
+    for (int i = 0; i < numThreads; i++) {
+      this.service.submit(new WriteSpanRunnable());
+    }
+  }
+
+  private class WriteSpanRunnable implements Runnable {
+    private RpcClient flumeClient = null;
+
+    /**
+     * This runnable sends a HTrace span to the Flume.
+     */
+    @Override
+    public void run() {
+      List<Span> dequeuedSpans = new ArrayList<Span>(maxSpanBatchSize);
+      long errorCount = 0;
+
+      while (running.get() || queue.size() > 0) {
+        Span firstSpan = null;
+        try {
+          // Block for up to a second. to try and get a span.
+          // We only block for a little bit in order to notice
+          // if the running value has changed
+          firstSpan = queue.poll(1, TimeUnit.SECONDS);
+
+          // If the poll was successful then it's possible that there
+          // will be other spans to get. Try and get them.
+          if (firstSpan != null) {
+            // Add the first one that we got
+            dequeuedSpans.add(firstSpan);
+            // Try and get up to 100 queues
+            queue.drainTo(dequeuedSpans, maxSpanBatchSize - 1);
+          }
+        } catch (InterruptedException ie) {
+          // Ignored.
+        }
+
+        startClient();
+        if (dequeuedSpans.isEmpty()) {
+          continue;
+        }
+
+        try {
+          List<Event> events = new ArrayList<Event>(dequeuedSpans.size());
+          for (Span span : dequeuedSpans) {
+            // Headers allow Flume to filter
+            Map<String, String> headers = new HashMap<String, String>();
+            headers.put("TraceId",      Long.toString(span.getTraceId()));
+            headers.put("SpanId",       Long.toString(span.getSpanId()));
+            headers.put("ProcessId",    span.getProcessId());
+            headers.put("Description",  span.getDescription());
+
+            String body = span.toJson();
+
+            Event evt = EventBuilder.withBody(body, Charset.forName("UTF-8"), headers);
+            events.add(evt);
+          }
+          flumeClient.appendBatch(events);
+
+          // clear the list for the next time through.
+          dequeuedSpans.clear();
+          // reset the error counter.
+          errorCount = 0;
+        } catch (Exception e) {
+          errorCount += 1;
+          // If there have been ten errors in a row start dropping things.
+          if (errorCount < MAX_ERRORS) {
+            try {
+              queue.addAll(dequeuedSpans);
+            } catch (IllegalStateException ex) {
+              LOG.error("Drop " + dequeuedSpans.size() +
+                        " span(s) because writing to HBase failed.");
+            }
+          }
+          closeClient();
+          try {
+            // Since there was an error sleep just a little bit to try and allow the
+            // HBase some time to recover.
+            Thread.sleep(500);
+          } catch (InterruptedException e1) {
+            // Ignored
+          }
+        }
+      }
+      closeClient();
+    }
+
+    /**
+     * Close Flume RPC client
+     */
+    private void closeClient() {
+      if (flumeClient != null) {
+        try {
+          flumeClient.close();
+        } catch (FlumeException ex) {
+          LOG.warn("Error while trying to close Flume Rpc Client.", ex);
+        } finally {
+          flumeClient = null;
+        }
+      }
+    }
+
+    /**
+     * Create / reconnect Flume RPC client
+     */
+    private void startClient() {
+      // If current client is inactive, close it
+      if (flumeClient != null && !flumeClient.isActive()) {
+        flumeClient.close();
+        flumeClient = null;
+      }
+      // Create client if needed
+      if (flumeClient == null) {
+        try {
+          flumeClient = RpcClientFactory.getDefaultInstance(flumeHostName, flumePort, maxSpanBatchSize);
+        } catch (FlumeException e) {
+          LOG.warn("Failed to create Flume RPC Client. " + e.getMessage());
+        }
+      }
+    }
+  }
+
+  /**
+   * Close the receiver.
+   * <p/>
+   * This tries to shutdown thread pool.
+   *
+   * @throws IOException
+   */
+  @Override
+  public void close() throws IOException {
+    running.set(false);
+    service.shutdown();
+    try {
+      if (!service.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) {
+        LOG.error("Was not able to process all remaining spans upon closing in: " +
+            SHUTDOWN_TIMEOUT + " " + TimeUnit.SECONDS +
+            ". Left Spans could be dropped.");
+       }
+    } catch (InterruptedException e1) {
+      LOG.warn("Thread interrupted when terminating executor.", e1);
+    }
+  }
+
+  @Override
+  public void receiveSpan(Span span) {
+    if (running.get()) {
+      try {
+        this.queue.add(span);
+      } catch (IllegalStateException e) {
+        LOG.error("Error trying to append span (" +
+            span.getDescription() +
+            ") to the queue. Blocking Queue was full.");
+      }
+    }
+  }
+}
diff --git a/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
new file mode 100644
index 0000000..a825690
--- /dev/null
+++ b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.impl;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.apache.avro.AvroRemoteException;
+import org.apache.avro.ipc.Server;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.flume.EventDeliveryException;
+import org.apache.flume.FlumeException;
+import org.apache.flume.api.RpcTestUtils;
+import org.apache.flume.source.avro.AvroFlumeEvent;
+import org.apache.flume.source.avro.AvroSourceProtocol;
+import org.apache.flume.source.avro.Status;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceCreator;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestFlumeSpanReceiver {
+  private static final Log LOG = LogFactory.getLog(TestFlumeSpanReceiver.class);
+
+  private static final String ROOT_SPAN_DESC = "ROOT";
+
+  private SpanReceiver spanReceiver;
+  private Server flumeServer;
+  private TraceCreator traceCreator;
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+  }
+
+  @Test
+  public void testSimpleTraces() throws FlumeException,
+      EventDeliveryException, IOException {
+    AvroHandler avroHandler = null;
+    List<Span> spans = null;
+    try {
+      avroHandler = new AvroHandler();
+      startReceiver(null, avroHandler);
+      
+      spans = new ArrayList<Span>();
+      Span rootSpan = new MilliSpan(ROOT_SPAN_DESC, 1, Span.ROOT_SPAN_ID, 100, "test");
+      Span innerOne = rootSpan.child("Some good work");
+      Span innerTwo = innerOne.child("Some more good work");
+      innerTwo.stop();
+      spans.add(innerTwo);
+      innerOne.stop();
+      spans.add(innerOne);
+      rootSpan.addKVAnnotation("foo".getBytes(), "bar".getBytes());
+      rootSpan.addTimelineAnnotation("timeline");
+      rootSpan.stop();
+      spans.add(rootSpan);
+
+    } finally {
+      stopReceiver();
+    }
+    List<AvroFlumeEvent> events = avroHandler.getAllEvents();
+    Assert.assertEquals(spans.size(), events.size());
+    for (int i = 0; i < spans.size(); i ++) {
+      String json = new String(events.get(i).getBody().array(), Charset.forName("UTF-8"));
+      Assert.assertTrue(json.contains(spans.get(i).getDescription()));
+    }
+  }
+
+  @Test
+  public void testConcurrency() throws FlumeException,
+      EventDeliveryException, IOException {
+    try {
+      Map<String, String> extraConf = new HashMap<String, String>();
+      extraConf.put(FlumeSpanReceiver.NUM_THREADS_KEY, "5");
+      startReceiver(extraConf, new RpcTestUtils.OKAvroHandler());
+      traceCreator.createThreadedTrace();
+    } finally {
+      stopReceiver();
+    }
+  }
+
+  @Test
+  public void testResilience() throws FlumeException,
+      EventDeliveryException, IOException {
+    try {
+      startReceiver(null, new RpcTestUtils.FailedAvroHandler());
+      traceCreator.createThreadedTrace();
+    } finally {
+      stopReceiver();
+    }
+  }
+
+  private void startReceiver(Map<String, String> extraConf, AvroSourceProtocol avroHandler) {
+    // Start Flume server
+    Assert.assertNull(flumeServer);
+    flumeServer = RpcTestUtils.startServer(avroHandler);
+
+    // Create and configure span receiver
+    Map<String, String> conf = new HashMap<String, String>();
+    conf.put(FlumeSpanReceiver.FLUME_HOSTNAME_KEY, "127.0.0.1");
+    conf.put(FlumeSpanReceiver.FLUME_PORT_KEY, Integer.toString(flumeServer.getPort()));
+    if (extraConf != null) {
+      conf.putAll(extraConf);
+    }
+    
+    spanReceiver = new FlumeSpanReceiver(HTraceConfiguration.fromMap(conf));
+
+    // Create trace creator, it will register our receiver
+    traceCreator = new TraceCreator(spanReceiver);
+  }
+
+  private void stopReceiver() throws IOException {
+    // Close span receiver
+    if (spanReceiver != null) {
+      Trace.removeReceiver(spanReceiver);
+      spanReceiver.close();
+      spanReceiver = null;
+    }
+
+    // Close Flume server
+    if (flumeServer != null) {
+      RpcTestUtils.stopServer(flumeServer);
+      flumeServer = null;
+    }
+  }
+  
+  private static class AvroHandler implements AvroSourceProtocol {
+    private ArrayList<AvroFlumeEvent> all_events = new ArrayList<AvroFlumeEvent>();
+    
+    public List<AvroFlumeEvent> getAllEvents() {
+      return new ArrayList<AvroFlumeEvent>(all_events);
+    }
+    
+    @Override
+    public Status append(AvroFlumeEvent event) throws AvroRemoteException {
+      all_events.add(event);
+      return Status.OK;
+    }
+
+    @Override
+    public Status appendBatch(List<AvroFlumeEvent> events) throws
+        AvroRemoteException {
+      all_events.addAll(events);
+      return Status.OK;
+    }
+  }
+}
diff --git a/htrace-hbase/README.md b/htrace-hbase/README.md
new file mode 100644
index 0000000..d0c41f5
--- /dev/null
+++ b/htrace-hbase/README.md
@@ -0,0 +1,166 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+htrace-hbase
+============
+
+htrace-hbase provides the span receiver which sends tracing spans to HBase
+and a viewer which retrieves spans from HBase and displays them graphically.
+
+
+Tutorial
+--------
+
+In the example below, we use the same HBase instance running in standalone-mode
+as both tracee and storage for the tracing spans.
+
+First, get HBase and build it:
+
+    $ git clone https://github.com/apache/hbase
+    $ cd hbase
+    $ mvn package -DskipTests
+
+Build htrace-hbase (by building all of htrace... just takes a second):
+
+    $ git clone git at github.com:cloudera/htrace.git
+    $ cd htrace.git
+    $ mvn install
+
+This will add the htrace jars including htrace-hbase to your local
+maven repository.
+
+Add a configuration that sets HBase as span receiver in hbase-site.xml:
+
+    <property>
+      <name>hbase.trace.spanreceiver.classes</name>
+      <value>org.apache.htrace.impl.HBaseSpanReceiver</value>
+    </property>
+
+Starting HBase server in standalone-mode with htrace-hbase jar added
+to the CLASSPATH (use appropriate 'version' -- below we are using 3.0.4).
+
+    $ HBASE_CLASSPATH=$HOME/.m2/repository/org/apache/htrace/htrace-hbase/3.0.4/htrace-hbase-3.0.4.jar $HBASE_HOME/bin/hbase master start
+
+Running HBase shell from another terminal, add the table in which
+tracing spans are stored.  By default it uses the table named
+"htrace" with two column families "s" and "i" by default:
+
+    hbase(main):001:0> create 'htrace', 's', 'i'
+
+Run some tracing from hbase shell (Make sure htrace is on the CLASSPATH when you start the shell):
+
+    $ HBASE_CLASSPATH=$HOME/.m2/repository/org/apache/htrace/htrace-hbase/3.0.4/htrace-hbase-3.0.4.jar ./bin/hbase shell
+
+    hbase(main):002:0> trace 'start'; create 't1', 'f'; trace 'stop'
+    ...
+    hbase(main):003:0> trace 'start'; put 't1', 'r1', 'f:c1', 'value'; trace 'stop'
+    ...
+
+Running the main class of receiver also generate a simple, artificial trace for test:
+
+    $ bin/hbase org.apache.htrace.impl.HBaseSpanReceiver
+
+Starting viewer process which listens 0.0.0.0:16900 by default.:
+
+    $ HBASE_CLASSPATH=$HOME/.m2/repository/org/apache/htrace/htrace-hbase/3.0.4/htrace-hbase-3.0.4.jar ./bin/hbase org.apache.htrace.viewer.HBaseSpanViewerServer
+
+Accessing http://host:16900/ with Web browser shows you list of traces like below.:
+
+![list of traces](traces.png "traces list")
+
+Clicking the trace in the list shows you the spans.:
+
+![visualization of spans](spans.png "spans view")
+
+Light blue rectangles represent spans.
+The horizontal position of the rectangle represents relative time.
+The width of a rectangle and the number at its lower left corner
+is the time from start to stop of the span in milliseconds.
+If you hover over any small red rectangle, you will see the annotation
+associated with the span in a popup window.
+
+![timeline annotations](timelines.png "timeline annotations")
+
+Leading numbers are time of annotation relative to start of the trace.
+
+
+Receiver Configuration
+----------------------
+
+Configurations for span receiver running in HBase
+to connect to the HBase to which spans are sent.
+These are different from the properties of usual HBase client:
+
+    <property>
+      <name>hbase.htrace.hbase.collector-quorum</name>
+      <value>127.0.0.1</value>
+    </property>
+    <property>
+      <name>hbase.htrace.hbase.zookeeper.property.clientPort</name>
+      <value>2181</value>
+    </property>
+    <property>
+      <name>hbase.htrace.hbase.zookeeper.znode.parent</name>
+      <value>/hbase</value>
+    </property>
+
+You can specify the name of table and column families
+for storing tracing spans by configurations shown below.:
+
+    <property>
+      <name>hbase.htrace.hbase.table</name>
+      <value>htrace</value>
+    </property>
+    <property>
+      <name>hbase.htrace.hbase.columnfamily</name>
+      <value>s</value>
+    </property>
+    <property>
+      <name>hbase.htrace.hbase.indexfamily</name>
+      <value>i</value>
+    </property>
+
+Notice that these configurations are prefixed by `hbase.`
+because the tracee are assumed to be HBase here.
+
+
+Viewer Configuration
+--------------------
+
+You can set listen address of span viewer server by `htrace.viewer.http.address`.
+In addition, span viewer server uses
+- usual HBase client configuration to connect to HBase and
+- receiver's configuration properties without prefix
+  to specify the name of table and column families.
+
+```
+$ bin/hbase org.apache.htrace.viewer.HBaseSpanViewerServer \
+    -Dhtrace.viewer.http.address=0.0.0.0:16900 \
+    -Dhbase.zookeeper.quorum=127.0.0.1 \
+    -Dhbase.zookeeper.znode.parent=/hbase \
+    -Dhtrace.hbase.table=htrace \
+    -Dhtrace.hbase.columnfamily=s \
+    -Dhtrace.hbase.indexfamily=i
+```
+
+Todo
+----
+
+- enabling to focus in/out specific spans in trace.
+- limiting the traces shown in list based on time period.
+- adding utility shell script.
+- adding tests.
diff --git a/htrace-hbase/pom.xml b/htrace-hbase/pom.xml
new file mode 100644
index 0000000..f068da7
--- /dev/null
+++ b/htrace-hbase/pom.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
+license agreements. See the NOTICE file distributed with this work for additional
+information regarding copyright ownership. The ASF licenses this file to
+You under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of
+the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
+by applicable law or agreed to in writing, software distributed under the
+License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, either express or implied. See the License for the specific
+language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>htrace-hbase</artifactId>
+  <packaging>jar</packaging>
+
+  <parent>
+    <artifactId>htrace</artifactId>
+    <groupId>org.apache.htrace</groupId>
+    <version>3.1.0-incubating</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <name>htrace-hbase</name>
+  <description>
+    htrace-hbase is the tools to send tracing information
+    to an HBase database for analysis later.
+  </description>
+  <url>http://incubator.apache.org/projects/htrace.html</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <hbase.version>0.99.2</hbase.version>
+    <!-- <hbase.version>0.99.0-SNAPSHOT</hbase.version> -->
+    <hadoop.version>2.4.0</hadoop.version>
+    <!-- <hadoop.version>3.0.0-SNAPSHOT</hadoop.version> -->
+    <protobuf.version>2.5.0</protobuf.version>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>${basedir}/src/main</directory>
+        <includes>
+          <include>webapps/**</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <!--Make it so assembly:single does nothing in here-->
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <skipAssembly>true</skipAssembly>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/generated/**</exclude>
+            <exclude>**/webapps/static/d3.min.js</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <descriptorRefs>
+            <descriptorRef>jar-with-dependencies</descriptorRef>
+          </descriptorRefs>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-gpg-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <!-- explicitly define maven-deploy-plugin after other to force exec order -->
+        <artifactId>maven-deploy-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <!-- Module deps. -->
+    <dependency>
+      <groupId>org.apache.htrace</groupId>
+      <artifactId>htrace-core</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.htrace</groupId>
+      <artifactId>htrace-core</artifactId>
+      <version>${project.version}</version>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+      <version>${protobuf.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <!-- Global deps. -->
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- HBase specific deps. -->
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-client</artifactId>
+      <version>${hbase.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.htrace</groupId>
+          <artifactId>htrace-core</artifactId>
+        </exclusion>
+      </exclusions> 
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-testing-util</artifactId>
+      <version>${hbase.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>compile-protobuf</id>
+      <activation>
+        <property>
+          <name>compile-protobuf</name>
+        </property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-maven-plugins</artifactId>
+            <version>${hadoop.version}</version>
+            <configuration>
+              <protocVersion>${protobuf.version}</protocVersion>
+              <protocCommand>${protoc.path}</protocCommand>
+            </configuration>
+            <executions>
+              <execution>
+                <id>compile-protoc</id>
+                <phase>generate-sources</phase>
+                <goals>
+                  <goal>protoc</goal>
+                </goals>
+                <configuration>
+                  <imports>
+                    <param>${basedir}/src/main/protobuf</param>
+                  </imports>
+                  <source>
+                    <directory>${basedir}/src/main/protobuf</directory>
+                    <includes>
+                      <include>Span.proto</include>
+                    </includes>
+                  </source>
+                  <output>${basedir}/src/main/java/</output>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/htrace-hbase/spans.png b/htrace-hbase/spans.png
new file mode 100644
index 0000000..ceac10e
Binary files /dev/null and b/htrace-hbase/spans.png differ
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/HBaseHTraceConfiguration.java b/htrace-hbase/src/main/java/org/apache/htrace/HBaseHTraceConfiguration.java
new file mode 100644
index 0000000..86401bf
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/HBaseHTraceConfiguration.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.htrace.HTraceConfiguration;
+
+/**
+ * Meshes {@link HTraceConfiguration} to {@link Configuration}
+ * @author stack
+ *
+ */
+public class HBaseHTraceConfiguration extends HTraceConfiguration {
+  public static final String KEY_PREFIX = "hbase.htrace.";
+  private final Configuration conf;
+
+  public HBaseHTraceConfiguration(Configuration conf) {
+    this.conf = conf;
+  }
+
+  @Override
+  public String get(String key) {
+    return conf.get(KEY_PREFIX + key);
+  }
+
+  @Override
+  public String get(String key, String defaultValue) {
+    return conf.get(KEY_PREFIX + key, defaultValue);
+
+  }
+
+  @Override
+  public boolean getBoolean(String key, boolean defaultValue) {
+    return conf.getBoolean(KEY_PREFIX + key, defaultValue);
+  }
+}
\ No newline at end of file
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java b/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java
new file mode 100644
index 0000000..a6c8382
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.Trace;
+
+/**
+ * This class provides functions for reading the names of SpanReceivers from
+ * hbase-site.xml, adding those SpanReceivers to the Tracer, and closing those
+ * SpanReceivers when appropriate.
+ */
+public class HBaseSpanReceiverHost {
+  public static final String SPAN_RECEIVERS_CONF_KEY = "hbase.trace.spanreceiver.classes";
+  private static final Log LOG = LogFactory.getLog(HBaseSpanReceiverHost.class);
+  private Collection<SpanReceiver> receivers;
+  private Configuration conf;
+  private boolean closed = false;
+
+  private static enum SingletonHolder {
+    INSTANCE;
+    Object lock = new Object();
+    HBaseSpanReceiverHost host = null;
+  }
+
+  public static HBaseSpanReceiverHost getInstance(Configuration conf) {
+    synchronized (SingletonHolder.INSTANCE.lock) {
+      if (SingletonHolder.INSTANCE.host != null) {
+        return SingletonHolder.INSTANCE.host;
+      }
+
+      HBaseSpanReceiverHost host = new HBaseSpanReceiverHost(conf);
+      host.loadSpanReceivers();
+      SingletonHolder.INSTANCE.host = host;
+      return SingletonHolder.INSTANCE.host;
+    }
+
+  }
+
+  HBaseSpanReceiverHost(Configuration conf) {
+    receivers = new HashSet<SpanReceiver>();
+    this.conf = conf;
+  }
+
+  /**
+   * Reads the names of classes specified in the
+   * "hbase.trace.spanreceiver.classes" property and instantiates and registers
+   * them with the Tracer as SpanReceiver's.
+   *
+   */
+  public void loadSpanReceivers() {
+    String[] receiverNames = conf.getStrings(SPAN_RECEIVERS_CONF_KEY);
+    if (receiverNames == null || receiverNames.length == 0) {
+      return;
+    }
+    SpanReceiverBuilder builder = new SpanReceiverBuilder(new HBaseHTraceConfiguration(this.conf));
+    for (String className : receiverNames) {
+      SpanReceiver receiver = builder.spanReceiverClass(className.trim()).build();
+      if (receiver != null) {
+        receivers.add(receiver);
+        LOG.info("SpanReceiver " + className + " was loaded successfully.");
+      }
+    }
+    for (SpanReceiver rcvr : receivers) {
+      Trace.addReceiver(rcvr);
+    }
+  }
+
+  /**
+   * Calls close() on all SpanReceivers created by this HBaseSpanReceiverHost.
+   */
+  public synchronized void closeReceivers() {
+    if (closed) return;
+    closed = true;
+    for (SpanReceiver rcvr : receivers) {
+      try {
+        rcvr.close();
+      } catch (IOException e) {
+        LOG.warn("Unable to close SpanReceiver correctly: " + e.getMessage(), e);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
new file mode 100644
index 0000000..b03291e
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.htrace.HBaseHTraceConfiguration;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Sampler;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.SpanReceiverBuilder;
+import org.apache.htrace.TimelineAnnotation;
+import org.apache.htrace.Trace;
+import org.apache.htrace.TraceScope;
+import org.apache.htrace.protobuf.generated.SpanProtos;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+/**
+ * HBase is an open source distributed datastore.
+ * This span receiver store spans into HBase.
+ * HTrace spans are queued into a blocking queue.
+ * From there background worker threads will send them
+ * to a HBase database.
+ */
+public class HBaseSpanReceiver implements SpanReceiver {
+  private static final Log LOG = LogFactory.getLog(HBaseSpanReceiver.class);
+
+  public static final String COLLECTOR_QUORUM_KEY = "htrace.hbase.collector-quorum";
+  public static final String DEFAULT_COLLECTOR_QUORUM = "127.0.0.1";
+  public static final String ZOOKEEPER_CLIENT_PORT_KEY = "htrace.hbase.zookeeper.property.clientPort";
+  public static final int DEFAULT_ZOOKEEPER_CLIENT_PORT = 2181;
+  public static final String ZOOKEEPER_ZNODE_PARENT_KEY = "htrace.hbase.zookeeper.znode.parent";
+  public static final String DEFAULT_ZOOKEEPER_ZNODE_PARENT = "/hbase";
+  public static final String NUM_THREADS_KEY = "htrace.hbase.num-threads";
+  public static final int DEFAULT_NUM_THREADS = 1;
+  public static final String MAX_SPAN_BATCH_SIZE_KEY = "htrace.hbase.batch.size";
+  public static final int DEFAULT_MAX_SPAN_BATCH_SIZE = 100;
+  public static final String TABLE_KEY = "htrace.hbase.table";
+  public static final String DEFAULT_TABLE = "htrace";
+  public static final String COLUMNFAMILY_KEY = "htrace.hbase.columnfamily";
+  public static final String DEFAULT_COLUMNFAMILY = "s";
+  public static final String INDEXFAMILY_KEY = "htrace.hbase.indexfamily";
+  public static final String DEFAULT_INDEXFAMILY = "i";
+  public static final byte[] INDEX_SPAN_QUAL = Bytes.toBytes("s");
+  public static final byte[] INDEX_TIME_QUAL = Bytes.toBytes("t");
+
+  /**
+   * How long this receiver will try and wait for all threads to shutdown.
+   */
+  private static final int SHUTDOWN_TIMEOUT = 30;
+
+  /**
+   * How many errors in a row before we start dropping traces on the floor.
+   */
+  private static final int MAX_ERRORS = 10;
+
+  /**
+   * The queue that will get all HTrace spans that are to be sent.
+   */
+  private final BlockingQueue<Span> queue;
+
+  /**
+   * Boolean used to signal that the threads should end.
+   */
+  private final AtomicBoolean running = new AtomicBoolean(true);
+
+  /**
+   * The thread factory used to create new ExecutorService.
+   * <p/>
+   * This will be the same factory for the lifetime of this object so that
+   * no thread names will ever be duplicated.
+   */
+  private final ThreadFactory tf;
+
+  private ExecutorService service;
+  private final HTraceConfiguration conf;
+  private final Configuration hconf;
+  private final byte[] table;
+  private final byte[] cf;
+  private final byte[] icf;
+  private final int maxSpanBatchSize;
+
+  public HBaseSpanReceiver(HTraceConfiguration conf) {
+    this.queue = new ArrayBlockingQueue<Span>(1000);
+    this.tf = new ThreadFactoryBuilder().setDaemon(true)
+                                        .setNameFormat("hbaseSpanReceiver-%d")
+                                        .build();
+    this.conf = conf;
+    this.hconf = HBaseConfiguration.create();
+    this.table = Bytes.toBytes(conf.get(TABLE_KEY, DEFAULT_TABLE));
+    this.cf = Bytes.toBytes(conf.get(COLUMNFAMILY_KEY, DEFAULT_COLUMNFAMILY));
+    this.icf = Bytes.toBytes(conf.get(INDEXFAMILY_KEY, DEFAULT_INDEXFAMILY));
+    this.maxSpanBatchSize = conf.getInt(MAX_SPAN_BATCH_SIZE_KEY,
+                                        DEFAULT_MAX_SPAN_BATCH_SIZE);
+    String quorum = conf.get(COLLECTOR_QUORUM_KEY, DEFAULT_COLLECTOR_QUORUM);
+    hconf.set(HConstants.ZOOKEEPER_QUORUM, quorum);
+    String znodeParent = conf.get(ZOOKEEPER_ZNODE_PARENT_KEY, DEFAULT_ZOOKEEPER_ZNODE_PARENT);
+    hconf.set(HConstants.ZOOKEEPER_ZNODE_PARENT, znodeParent);
+    int clientPort = conf.getInt(ZOOKEEPER_CLIENT_PORT_KEY, DEFAULT_ZOOKEEPER_CLIENT_PORT);
+    hconf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, clientPort);
+
+    // If there are already threads runnnig tear them down.
+    if (this.service != null) {
+      this.service.shutdownNow();
+      this.service = null;
+    }
+    int numThreads = conf.getInt(NUM_THREADS_KEY, DEFAULT_NUM_THREADS);
+    this.service = Executors.newFixedThreadPool(numThreads, tf);
+    for (int i = 0; i < numThreads; i++) {
+      this.service.submit(new WriteSpanRunnable());
+    }
+  }
+
+  private class WriteSpanRunnable implements Runnable {
+    private Connection hconnection;
+    private Table htable;
+
+    public WriteSpanRunnable() {
+    }
+
+    /**
+     * This runnable sends a HTrace span to the HBase.
+     */
+    @Override
+    public void run() {
+      SpanProtos.Span.Builder sbuilder = SpanProtos.Span.newBuilder();
+      SpanProtos.TimelineAnnotation.Builder tlbuilder =
+          SpanProtos.TimelineAnnotation.newBuilder();
+      List<Span> dequeuedSpans = new ArrayList<Span>(maxSpanBatchSize);
+      long errorCount = 0;
+
+      while (running.get() || queue.size() > 0) {
+        Span firstSpan = null;
+        try {
+          // Block for up to a second. to try and get a span.
+          // We only block for a little bit in order to notice
+          // if the running value has changed
+          firstSpan = queue.poll(1, TimeUnit.SECONDS);
+
+          // If the poll was successful then it's possible that there
+          // will be other spans to get. Try and get them.
+          if (firstSpan != null) {
+            // Add the first one that we got
+            dequeuedSpans.add(firstSpan);
+            // Try and get up to 100 queues
+            queue.drainTo(dequeuedSpans, maxSpanBatchSize - 1);
+          }
+        } catch (InterruptedException ie) {
+          // Ignored.
+        }
+        startClient();
+        if (dequeuedSpans.isEmpty()) {
+          try {
+            this.htable.flushCommits();
+          } catch (IOException e) {
+            LOG.error("failed to flush writes to HBase.");
+            closeClient();
+          }
+          continue;
+        }
+
+        try {
+          for (Span span : dequeuedSpans) {
+            sbuilder.clear()
+                    .setTraceId(span.getTraceId())
+                    .setParentId(span.getParentId())
+                    .setStart(span.getStartTimeMillis())
+                    .setStop(span.getStopTimeMillis())
+                    .setSpanId(span.getSpanId())
+                    .setProcessId(span.getProcessId())
+                    .setDescription(span.getDescription());
+            for (TimelineAnnotation ta : span.getTimelineAnnotations()) {
+              sbuilder.addTimeline(tlbuilder.clear()
+                                            .setTime(ta.getTime())
+                                            .setMessage(ta.getMessage())
+                                            .build());
+            }
+            Put put = new Put(Bytes.toBytes(span.getTraceId()));
+            put.add(HBaseSpanReceiver.this.cf,
+                    sbuilder.build().toByteArray(),
+                    null);
+            if (span.getParentId() == Span.ROOT_SPAN_ID) {
+              put.add(HBaseSpanReceiver.this.icf,
+                      INDEX_TIME_QUAL,
+                      Bytes.toBytes(span.getStartTimeMillis()));
+              put.add(HBaseSpanReceiver.this.icf,
+                      INDEX_SPAN_QUAL,
+                      sbuilder.build().toByteArray());
+            }
+            this.htable.put(put);
+          }
+          // clear the list for the next time through.
+          dequeuedSpans.clear();
+          // reset the error counter.
+          errorCount = 0;
+        } catch (Exception e) {
+          errorCount += 1;
+          // If there have been ten errors in a row start dropping things.
+          if (errorCount < MAX_ERRORS) {
+            try {
+              queue.addAll(dequeuedSpans);
+            } catch (IllegalStateException ex) {
+              LOG.error("Drop " + dequeuedSpans.size() +
+                        " span(s) because writing to HBase failed.");
+            }
+          }
+          closeClient();
+          try {
+            // Since there was an error sleep just a little bit to try and allow the
+            // HBase some time to recover.
+            Thread.sleep(500);
+          } catch (InterruptedException e1) {
+            // Ignored
+          }
+        }
+      }
+      closeClient();
+    }
+
+    /**
+     * Close out the connection.
+     */
+    private void closeClient() {
+      // close out the transport.
+      try {
+        if (this.htable != null) {
+          this.htable.close();
+          this.htable = null;
+        }
+        if (this.hconnection != null) {
+          this.hconnection.close();
+          this.hconnection = null;
+        }
+      } catch (IOException e) {
+        LOG.warn("Failed to close HBase connection. " + e.getMessage());
+      }
+    }
+
+    /**
+     * Re-connect to HBase
+     */
+    private void startClient() {
+      if (this.htable == null) {
+        try {
+          hconnection = ConnectionFactory.createConnection(hconf);
+          htable = hconnection.getTable(TableName.valueOf(table));
+        } catch (IOException e) {
+          LOG.warn("Failed to create HBase connection. " + e.getMessage());
+        }
+      }
+    }
+  }
+
+  /**
+   * Close the receiver.
+   * <p/>
+   * This tries to shutdown thread pool.
+   *
+   * @throws IOException
+   */
+  @Override
+  public void close() throws IOException {
+    running.set(false);
+    service.shutdown();
+    try {
+      if (!service.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) {
+        LOG.error("Was not able to process all remaining spans upon closing in: " +
+            SHUTDOWN_TIMEOUT + " " + TimeUnit.SECONDS +
+            ". Left Spans could be dropped.");
+       }
+    } catch (InterruptedException e1) {
+      LOG.warn("Thread interrupted when terminating executor.", e1);
+    }
+  }
+
+  @Override
+  public void receiveSpan(Span span) {
+    if (running.get()) {
+      try {
+        this.queue.add(span);
+      } catch (IllegalStateException e) {
+        // todo: supress repeating error logs.
+        LOG.error("Error trying to append span (" +
+            span.getDescription() +
+            ") to the queue. Blocking Queue was full.");
+      }
+    }
+  }
+
+  /**
+   * Run basic test. Adds span to an existing htrace table in an existing hbase setup.
+   * Requires a running hbase to send the traces too with an already created trace
+   * table (Default table name is 'htrace' with column families 's' and 'i').
+   * @throws IOException
+   */
+  public static void main(String[] args) throws Exception {
+    SpanReceiverBuilder builder =
+      new SpanReceiverBuilder(new HBaseHTraceConfiguration(HBaseConfiguration.create()));
+    SpanReceiver receiver =
+      builder.spanReceiverClass(HBaseSpanReceiver.class.getName()).build();
+    Trace.addReceiver(receiver);
+    TraceScope parent = Trace.startSpan("HBaseSpanReceiver.main.parent", Sampler.ALWAYS);
+    Thread.sleep(10);
+    long traceid = parent.getSpan().getTraceId();
+    TraceScope child1 = Trace.startSpan("HBaseSpanReceiver.main.child.1");
+    Thread.sleep(10);
+    TraceScope child2 = Trace.startSpan("HBaseSpanReceiver.main.child.2", parent.getSpan());
+    Thread.sleep(10);
+    TraceScope gchild = Trace.startSpan("HBaseSpanReceiver.main.grandchild");
+    Trace.addTimelineAnnotation("annotation 1.");
+    Thread.sleep(10);
+    Trace.addTimelineAnnotation("annotation 2.");
+    gchild.close();
+    Thread.sleep(10);
+    child2.close();
+    Thread.sleep(10);
+    child1.close();
+    parent.close();
+    receiver.close();
+    System.out.println("trace id: " + traceid);
+  }
+}
\ No newline at end of file
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/protobuf/generated/SpanProtos.java b/htrace-hbase/src/main/java/org/apache/htrace/protobuf/generated/SpanProtos.java
new file mode 100644
index 0000000..98b8adb
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/protobuf/generated/SpanProtos.java
@@ -0,0 +1,2241 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: Span.proto
+
+package org.apache.htrace.protobuf.generated;
+
+public final class SpanProtos {
+  private SpanProtos() {}
+  public static void registerAllExtensions(
+      com.google.protobuf.ExtensionRegistry registry) {
+  }
+  public interface TimelineAnnotationOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // required int64 time = 1;
+    /**
+     * <code>required int64 time = 1;</code>
+     */
+    boolean hasTime();
+    /**
+     * <code>required int64 time = 1;</code>
+     */
+    long getTime();
+
+    // required string message = 2;
+    /**
+     * <code>required string message = 2;</code>
+     */
+    boolean hasMessage();
+    /**
+     * <code>required string message = 2;</code>
+     */
+    java.lang.String getMessage();
+    /**
+     * <code>required string message = 2;</code>
+     */
+    com.google.protobuf.ByteString
+        getMessageBytes();
+  }
+  /**
+   * Protobuf type {@code TimelineAnnotation}
+   */
+  public static final class TimelineAnnotation extends
+      com.google.protobuf.GeneratedMessage
+      implements TimelineAnnotationOrBuilder {
+    // Use TimelineAnnotation.newBuilder() to construct.
+    private TimelineAnnotation(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private TimelineAnnotation(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final TimelineAnnotation defaultInstance;
+    public static TimelineAnnotation getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public TimelineAnnotation getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private TimelineAnnotation(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              time_ = input.readInt64();
+              break;
+            }
+            case 18: {
+              bitField0_ |= 0x00000002;
+              message_ = input.readBytes();
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_TimelineAnnotation_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_TimelineAnnotation_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.class, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<TimelineAnnotation> PARSER =
+        new com.google.protobuf.AbstractParser<TimelineAnnotation>() {
+      public TimelineAnnotation parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new TimelineAnnotation(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<TimelineAnnotation> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // required int64 time = 1;
+    public static final int TIME_FIELD_NUMBER = 1;
+    private long time_;
+    /**
+     * <code>required int64 time = 1;</code>
+     */
+    public boolean hasTime() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>required int64 time = 1;</code>
+     */
+    public long getTime() {
+      return time_;
+    }
+
+    // required string message = 2;
+    public static final int MESSAGE_FIELD_NUMBER = 2;
+    private java.lang.Object message_;
+    /**
+     * <code>required string message = 2;</code>
+     */
+    public boolean hasMessage() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    /**
+     * <code>required string message = 2;</code>
+     */
+    public java.lang.String getMessage() {
+      java.lang.Object ref = message_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          message_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string message = 2;</code>
+     */
+    public com.google.protobuf.ByteString
+        getMessageBytes() {
+      java.lang.Object ref = message_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        message_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    private void initFields() {
+      time_ = 0L;
+      message_ = "";
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (!hasTime()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasMessage()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(1, time_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeBytes(2, getMessageBytes());
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(1, time_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(2, getMessageBytes());
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation)) {
+        return super.equals(obj);
+      }
+      org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation other = (org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation) obj;
+
+      boolean result = true;
+      result = result && (hasTime() == other.hasTime());
+      if (hasTime()) {
+        result = result && (getTime()
+            == other.getTime());
+      }
+      result = result && (hasMessage() == other.hasMessage());
+      if (hasMessage()) {
+        result = result && getMessage()
+            .equals(other.getMessage());
+      }
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasTime()) {
+        hash = (37 * hash) + TIME_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getTime());
+      }
+      if (hasMessage()) {
+        hash = (37 * hash) + MESSAGE_FIELD_NUMBER;
+        hash = (53 * hash) + getMessage().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code TimelineAnnotation}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_TimelineAnnotation_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_TimelineAnnotation_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.class, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder.class);
+      }
+
+      // Construct using org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        time_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        message_ = "";
+        bitField0_ = (bitField0_ & ~0x00000002);
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_TimelineAnnotation_descriptor;
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation getDefaultInstanceForType() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.getDefaultInstance();
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation build() {
+        org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation buildPartial() {
+        org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation result = new org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.time_ = time_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.message_ = message_;
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation) {
+          return mergeFrom((org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation other) {
+        if (other == org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.getDefaultInstance()) return this;
+        if (other.hasTime()) {
+          setTime(other.getTime());
+        }
+        if (other.hasMessage()) {
+          bitField0_ |= 0x00000002;
+          message_ = other.message_;
+          onChanged();
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (!hasTime()) {
+          
+          return false;
+        }
+        if (!hasMessage()) {
+          
+          return false;
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // required int64 time = 1;
+      private long time_ ;
+      /**
+       * <code>required int64 time = 1;</code>
+       */
+      public boolean hasTime() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>required int64 time = 1;</code>
+       */
+      public long getTime() {
+        return time_;
+      }
+      /**
+       * <code>required int64 time = 1;</code>
+       */
+      public Builder setTime(long value) {
+        bitField0_ |= 0x00000001;
+        time_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 time = 1;</code>
+       */
+      public Builder clearTime() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        time_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required string message = 2;
+      private java.lang.Object message_ = "";
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public boolean hasMessage() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public java.lang.String getMessage() {
+        java.lang.Object ref = message_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          message_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public com.google.protobuf.ByteString
+          getMessageBytes() {
+        java.lang.Object ref = message_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          message_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public Builder setMessage(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000002;
+        message_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public Builder clearMessage() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        message_ = getDefaultInstance().getMessage();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string message = 2;</code>
+       */
+      public Builder setMessageBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000002;
+        message_ = value;
+        onChanged();
+        return this;
+      }
+
+      // @@protoc_insertion_point(builder_scope:TimelineAnnotation)
+    }
+
+    static {
+      defaultInstance = new TimelineAnnotation(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:TimelineAnnotation)
+  }
+
+  public interface SpanOrBuilder
+      extends com.google.protobuf.MessageOrBuilder {
+
+    // required int64 trace_id = 1;
+    /**
+     * <code>required int64 trace_id = 1;</code>
+     */
+    boolean hasTraceId();
+    /**
+     * <code>required int64 trace_id = 1;</code>
+     */
+    long getTraceId();
+
+    // required int64 parent_id = 2;
+    /**
+     * <code>required int64 parent_id = 2;</code>
+     */
+    boolean hasParentId();
+    /**
+     * <code>required int64 parent_id = 2;</code>
+     */
+    long getParentId();
+
+    // required int64 start = 3;
+    /**
+     * <code>required int64 start = 3;</code>
+     */
+    boolean hasStart();
+    /**
+     * <code>required int64 start = 3;</code>
+     */
+    long getStart();
+
+    // required int64 stop = 4;
+    /**
+     * <code>required int64 stop = 4;</code>
+     */
+    boolean hasStop();
+    /**
+     * <code>required int64 stop = 4;</code>
+     */
+    long getStop();
+
+    // required int64 span_id = 5;
+    /**
+     * <code>required int64 span_id = 5;</code>
+     */
+    boolean hasSpanId();
+    /**
+     * <code>required int64 span_id = 5;</code>
+     */
+    long getSpanId();
+
+    // required string process_id = 6;
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    boolean hasProcessId();
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    java.lang.String getProcessId();
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    com.google.protobuf.ByteString
+        getProcessIdBytes();
+
+    // required string description = 7;
+    /**
+     * <code>required string description = 7;</code>
+     */
+    boolean hasDescription();
+    /**
+     * <code>required string description = 7;</code>
+     */
+    java.lang.String getDescription();
+    /**
+     * <code>required string description = 7;</code>
+     */
+    com.google.protobuf.ByteString
+        getDescriptionBytes();
+
+    // repeated .TimelineAnnotation timeline = 8;
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> 
+        getTimelineList();
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation getTimeline(int index);
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    int getTimelineCount();
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    java.util.List<? extends org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder> 
+        getTimelineOrBuilderList();
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder getTimelineOrBuilder(
+        int index);
+  }
+  /**
+   * Protobuf type {@code Span}
+   */
+  public static final class Span extends
+      com.google.protobuf.GeneratedMessage
+      implements SpanOrBuilder {
+    // Use Span.newBuilder() to construct.
+    private Span(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
+      super(builder);
+      this.unknownFields = builder.getUnknownFields();
+    }
+    private Span(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }
+
+    private static final Span defaultInstance;
+    public static Span getDefaultInstance() {
+      return defaultInstance;
+    }
+
+    public Span getDefaultInstanceForType() {
+      return defaultInstance;
+    }
+
+    private final com.google.protobuf.UnknownFieldSet unknownFields;
+    @java.lang.Override
+    public final com.google.protobuf.UnknownFieldSet
+        getUnknownFields() {
+      return this.unknownFields;
+    }
+    private Span(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      initFields();
+      int mutable_bitField0_ = 0;
+      com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+          com.google.protobuf.UnknownFieldSet.newBuilder();
+      try {
+        boolean done = false;
+        while (!done) {
+          int tag = input.readTag();
+          switch (tag) {
+            case 0:
+              done = true;
+              break;
+            default: {
+              if (!parseUnknownField(input, unknownFields,
+                                     extensionRegistry, tag)) {
+                done = true;
+              }
+              break;
+            }
+            case 8: {
+              bitField0_ |= 0x00000001;
+              traceId_ = input.readInt64();
+              break;
+            }
+            case 16: {
+              bitField0_ |= 0x00000002;
+              parentId_ = input.readInt64();
+              break;
+            }
+            case 24: {
+              bitField0_ |= 0x00000004;
+              start_ = input.readInt64();
+              break;
+            }
+            case 32: {
+              bitField0_ |= 0x00000008;
+              stop_ = input.readInt64();
+              break;
+            }
+            case 40: {
+              bitField0_ |= 0x00000010;
+              spanId_ = input.readInt64();
+              break;
+            }
+            case 50: {
+              bitField0_ |= 0x00000020;
+              processId_ = input.readBytes();
+              break;
+            }
+            case 58: {
+              bitField0_ |= 0x00000040;
+              description_ = input.readBytes();
+              break;
+            }
+            case 66: {
+              if (!((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
+                timeline_ = new java.util.ArrayList<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation>();
+                mutable_bitField0_ |= 0x00000080;
+              }
+              timeline_.add(input.readMessage(org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.PARSER, extensionRegistry));
+              break;
+            }
+          }
+        }
+      } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+        throw e.setUnfinishedMessage(this);
+      } catch (java.io.IOException e) {
+        throw new com.google.protobuf.InvalidProtocolBufferException(
+            e.getMessage()).setUnfinishedMessage(this);
+      } finally {
+        if (((mutable_bitField0_ & 0x00000080) == 0x00000080)) {
+          timeline_ = java.util.Collections.unmodifiableList(timeline_);
+        }
+        this.unknownFields = unknownFields.build();
+        makeExtensionsImmutable();
+      }
+    }
+    public static final com.google.protobuf.Descriptors.Descriptor
+        getDescriptor() {
+      return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_Span_descriptor;
+    }
+
+    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+        internalGetFieldAccessorTable() {
+      return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_Span_fieldAccessorTable
+          .ensureFieldAccessorsInitialized(
+              org.apache.htrace.protobuf.generated.SpanProtos.Span.class, org.apache.htrace.protobuf.generated.SpanProtos.Span.Builder.class);
+    }
+
+    public static com.google.protobuf.Parser<Span> PARSER =
+        new com.google.protobuf.AbstractParser<Span>() {
+      public Span parsePartialFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws com.google.protobuf.InvalidProtocolBufferException {
+        return new Span(input, extensionRegistry);
+      }
+    };
+
+    @java.lang.Override
+    public com.google.protobuf.Parser<Span> getParserForType() {
+      return PARSER;
+    }
+
+    private int bitField0_;
+    // required int64 trace_id = 1;
+    public static final int TRACE_ID_FIELD_NUMBER = 1;
+    private long traceId_;
+    /**
+     * <code>required int64 trace_id = 1;</code>
+     */
+    public boolean hasTraceId() {
+      return ((bitField0_ & 0x00000001) == 0x00000001);
+    }
+    /**
+     * <code>required int64 trace_id = 1;</code>
+     */
+    public long getTraceId() {
+      return traceId_;
+    }
+
+    // required int64 parent_id = 2;
+    public static final int PARENT_ID_FIELD_NUMBER = 2;
+    private long parentId_;
+    /**
+     * <code>required int64 parent_id = 2;</code>
+     */
+    public boolean hasParentId() {
+      return ((bitField0_ & 0x00000002) == 0x00000002);
+    }
+    /**
+     * <code>required int64 parent_id = 2;</code>
+     */
+    public long getParentId() {
+      return parentId_;
+    }
+
+    // required int64 start = 3;
+    public static final int START_FIELD_NUMBER = 3;
+    private long start_;
+    /**
+     * <code>required int64 start = 3;</code>
+     */
+    public boolean hasStart() {
+      return ((bitField0_ & 0x00000004) == 0x00000004);
+    }
+    /**
+     * <code>required int64 start = 3;</code>
+     */
+    public long getStart() {
+      return start_;
+    }
+
+    // required int64 stop = 4;
+    public static final int STOP_FIELD_NUMBER = 4;
+    private long stop_;
+    /**
+     * <code>required int64 stop = 4;</code>
+     */
+    public boolean hasStop() {
+      return ((bitField0_ & 0x00000008) == 0x00000008);
+    }
+    /**
+     * <code>required int64 stop = 4;</code>
+     */
+    public long getStop() {
+      return stop_;
+    }
+
+    // required int64 span_id = 5;
+    public static final int SPAN_ID_FIELD_NUMBER = 5;
+    private long spanId_;
+    /**
+     * <code>required int64 span_id = 5;</code>
+     */
+    public boolean hasSpanId() {
+      return ((bitField0_ & 0x00000010) == 0x00000010);
+    }
+    /**
+     * <code>required int64 span_id = 5;</code>
+     */
+    public long getSpanId() {
+      return spanId_;
+    }
+
+    // required string process_id = 6;
+    public static final int PROCESS_ID_FIELD_NUMBER = 6;
+    private java.lang.Object processId_;
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    public boolean hasProcessId() {
+      return ((bitField0_ & 0x00000020) == 0x00000020);
+    }
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    public java.lang.String getProcessId() {
+      java.lang.Object ref = processId_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          processId_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string process_id = 6;</code>
+     */
+    public com.google.protobuf.ByteString
+        getProcessIdBytes() {
+      java.lang.Object ref = processId_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        processId_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    // required string description = 7;
+    public static final int DESCRIPTION_FIELD_NUMBER = 7;
+    private java.lang.Object description_;
+    /**
+     * <code>required string description = 7;</code>
+     */
+    public boolean hasDescription() {
+      return ((bitField0_ & 0x00000040) == 0x00000040);
+    }
+    /**
+     * <code>required string description = 7;</code>
+     */
+    public java.lang.String getDescription() {
+      java.lang.Object ref = description_;
+      if (ref instanceof java.lang.String) {
+        return (java.lang.String) ref;
+      } else {
+        com.google.protobuf.ByteString bs = 
+            (com.google.protobuf.ByteString) ref;
+        java.lang.String s = bs.toStringUtf8();
+        if (bs.isValidUtf8()) {
+          description_ = s;
+        }
+        return s;
+      }
+    }
+    /**
+     * <code>required string description = 7;</code>
+     */
+    public com.google.protobuf.ByteString
+        getDescriptionBytes() {
+      java.lang.Object ref = description_;
+      if (ref instanceof java.lang.String) {
+        com.google.protobuf.ByteString b = 
+            com.google.protobuf.ByteString.copyFromUtf8(
+                (java.lang.String) ref);
+        description_ = b;
+        return b;
+      } else {
+        return (com.google.protobuf.ByteString) ref;
+      }
+    }
+
+    // repeated .TimelineAnnotation timeline = 8;
+    public static final int TIMELINE_FIELD_NUMBER = 8;
+    private java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> timeline_;
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    public java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> getTimelineList() {
+      return timeline_;
+    }
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    public java.util.List<? extends org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder> 
+        getTimelineOrBuilderList() {
+      return timeline_;
+    }
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    public int getTimelineCount() {
+      return timeline_.size();
+    }
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation getTimeline(int index) {
+      return timeline_.get(index);
+    }
+    /**
+     * <code>repeated .TimelineAnnotation timeline = 8;</code>
+     */
+    public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder getTimelineOrBuilder(
+        int index) {
+      return timeline_.get(index);
+    }
+
+    private void initFields() {
+      traceId_ = 0L;
+      parentId_ = 0L;
+      start_ = 0L;
+      stop_ = 0L;
+      spanId_ = 0L;
+      processId_ = "";
+      description_ = "";
+      timeline_ = java.util.Collections.emptyList();
+    }
+    private byte memoizedIsInitialized = -1;
+    public final boolean isInitialized() {
+      byte isInitialized = memoizedIsInitialized;
+      if (isInitialized != -1) return isInitialized == 1;
+
+      if (!hasTraceId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasParentId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasStart()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasStop()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasSpanId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasProcessId()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      if (!hasDescription()) {
+        memoizedIsInitialized = 0;
+        return false;
+      }
+      for (int i = 0; i < getTimelineCount(); i++) {
+        if (!getTimeline(i).isInitialized()) {
+          memoizedIsInitialized = 0;
+          return false;
+        }
+      }
+      memoizedIsInitialized = 1;
+      return true;
+    }
+
+    public void writeTo(com.google.protobuf.CodedOutputStream output)
+                        throws java.io.IOException {
+      getSerializedSize();
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        output.writeInt64(1, traceId_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        output.writeInt64(2, parentId_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        output.writeInt64(3, start_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        output.writeInt64(4, stop_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        output.writeInt64(5, spanId_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        output.writeBytes(6, getProcessIdBytes());
+      }
+      if (((bitField0_ & 0x00000040) == 0x00000040)) {
+        output.writeBytes(7, getDescriptionBytes());
+      }
+      for (int i = 0; i < timeline_.size(); i++) {
+        output.writeMessage(8, timeline_.get(i));
+      }
+      getUnknownFields().writeTo(output);
+    }
+
+    private int memoizedSerializedSize = -1;
+    public int getSerializedSize() {
+      int size = memoizedSerializedSize;
+      if (size != -1) return size;
+
+      size = 0;
+      if (((bitField0_ & 0x00000001) == 0x00000001)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(1, traceId_);
+      }
+      if (((bitField0_ & 0x00000002) == 0x00000002)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(2, parentId_);
+      }
+      if (((bitField0_ & 0x00000004) == 0x00000004)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(3, start_);
+      }
+      if (((bitField0_ & 0x00000008) == 0x00000008)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(4, stop_);
+      }
+      if (((bitField0_ & 0x00000010) == 0x00000010)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt64Size(5, spanId_);
+      }
+      if (((bitField0_ & 0x00000020) == 0x00000020)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(6, getProcessIdBytes());
+      }
+      if (((bitField0_ & 0x00000040) == 0x00000040)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeBytesSize(7, getDescriptionBytes());
+      }
+      for (int i = 0; i < timeline_.size(); i++) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeMessageSize(8, timeline_.get(i));
+      }
+      size += getUnknownFields().getSerializedSize();
+      memoizedSerializedSize = size;
+      return size;
+    }
+
+    private static final long serialVersionUID = 0L;
+    @java.lang.Override
+    protected java.lang.Object writeReplace()
+        throws java.io.ObjectStreamException {
+      return super.writeReplace();
+    }
+
+    @java.lang.Override
+    public boolean equals(final java.lang.Object obj) {
+      if (obj == this) {
+       return true;
+      }
+      if (!(obj instanceof org.apache.htrace.protobuf.generated.SpanProtos.Span)) {
+        return super.equals(obj);
+      }
+      org.apache.htrace.protobuf.generated.SpanProtos.Span other = (org.apache.htrace.protobuf.generated.SpanProtos.Span) obj;
+
+      boolean result = true;
+      result = result && (hasTraceId() == other.hasTraceId());
+      if (hasTraceId()) {
+        result = result && (getTraceId()
+            == other.getTraceId());
+      }
+      result = result && (hasParentId() == other.hasParentId());
+      if (hasParentId()) {
+        result = result && (getParentId()
+            == other.getParentId());
+      }
+      result = result && (hasStart() == other.hasStart());
+      if (hasStart()) {
+        result = result && (getStart()
+            == other.getStart());
+      }
+      result = result && (hasStop() == other.hasStop());
+      if (hasStop()) {
+        result = result && (getStop()
+            == other.getStop());
+      }
+      result = result && (hasSpanId() == other.hasSpanId());
+      if (hasSpanId()) {
+        result = result && (getSpanId()
+            == other.getSpanId());
+      }
+      result = result && (hasProcessId() == other.hasProcessId());
+      if (hasProcessId()) {
+        result = result && getProcessId()
+            .equals(other.getProcessId());
+      }
+      result = result && (hasDescription() == other.hasDescription());
+      if (hasDescription()) {
+        result = result && getDescription()
+            .equals(other.getDescription());
+      }
+      result = result && getTimelineList()
+          .equals(other.getTimelineList());
+      result = result &&
+          getUnknownFields().equals(other.getUnknownFields());
+      return result;
+    }
+
+    private int memoizedHashCode = 0;
+    @java.lang.Override
+    public int hashCode() {
+      if (memoizedHashCode != 0) {
+        return memoizedHashCode;
+      }
+      int hash = 41;
+      hash = (19 * hash) + getDescriptorForType().hashCode();
+      if (hasTraceId()) {
+        hash = (37 * hash) + TRACE_ID_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getTraceId());
+      }
+      if (hasParentId()) {
+        hash = (37 * hash) + PARENT_ID_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getParentId());
+      }
+      if (hasStart()) {
+        hash = (37 * hash) + START_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getStart());
+      }
+      if (hasStop()) {
+        hash = (37 * hash) + STOP_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getStop());
+      }
+      if (hasSpanId()) {
+        hash = (37 * hash) + SPAN_ID_FIELD_NUMBER;
+        hash = (53 * hash) + hashLong(getSpanId());
+      }
+      if (hasProcessId()) {
+        hash = (37 * hash) + PROCESS_ID_FIELD_NUMBER;
+        hash = (53 * hash) + getProcessId().hashCode();
+      }
+      if (hasDescription()) {
+        hash = (37 * hash) + DESCRIPTION_FIELD_NUMBER;
+        hash = (53 * hash) + getDescription().hashCode();
+      }
+      if (getTimelineCount() > 0) {
+        hash = (37 * hash) + TIMELINE_FIELD_NUMBER;
+        hash = (53 * hash) + getTimelineList().hashCode();
+      }
+      hash = (29 * hash) + getUnknownFields().hashCode();
+      memoizedHashCode = hash;
+      return hash;
+    }
+
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        com.google.protobuf.ByteString data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        com.google.protobuf.ByteString data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(byte[] data)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        byte[] data,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws com.google.protobuf.InvalidProtocolBufferException {
+      return PARSER.parseFrom(data, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseDelimitedFrom(java.io.InputStream input)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseDelimitedFrom(
+        java.io.InputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseDelimitedFrom(input, extensionRegistry);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        com.google.protobuf.CodedInputStream input)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input);
+    }
+    public static org.apache.htrace.protobuf.generated.SpanProtos.Span parseFrom(
+        com.google.protobuf.CodedInputStream input,
+        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+        throws java.io.IOException {
+      return PARSER.parseFrom(input, extensionRegistry);
+    }
+
+    public static Builder newBuilder() { return Builder.create(); }
+    public Builder newBuilderForType() { return newBuilder(); }
+    public static Builder newBuilder(org.apache.htrace.protobuf.generated.SpanProtos.Span prototype) {
+      return newBuilder().mergeFrom(prototype);
+    }
+    public Builder toBuilder() { return newBuilder(this); }
+
+    @java.lang.Override
+    protected Builder newBuilderForType(
+        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+      Builder builder = new Builder(parent);
+      return builder;
+    }
+    /**
+     * Protobuf type {@code Span}
+     */
+    public static final class Builder extends
+        com.google.protobuf.GeneratedMessage.Builder<Builder>
+       implements org.apache.htrace.protobuf.generated.SpanProtos.SpanOrBuilder {
+      public static final com.google.protobuf.Descriptors.Descriptor
+          getDescriptor() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_Span_descriptor;
+      }
+
+      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
+          internalGetFieldAccessorTable() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_Span_fieldAccessorTable
+            .ensureFieldAccessorsInitialized(
+                org.apache.htrace.protobuf.generated.SpanProtos.Span.class, org.apache.htrace.protobuf.generated.SpanProtos.Span.Builder.class);
+      }
+
+      // Construct using org.apache.htrace.protobuf.generated.SpanProtos.Span.newBuilder()
+      private Builder() {
+        maybeForceBuilderInitialization();
+      }
+
+      private Builder(
+          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
+        super(parent);
+        maybeForceBuilderInitialization();
+      }
+      private void maybeForceBuilderInitialization() {
+        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
+          getTimelineFieldBuilder();
+        }
+      }
+      private static Builder create() {
+        return new Builder();
+      }
+
+      public Builder clear() {
+        super.clear();
+        traceId_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000001);
+        parentId_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000002);
+        start_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000004);
+        stop_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000008);
+        spanId_ = 0L;
+        bitField0_ = (bitField0_ & ~0x00000010);
+        processId_ = "";
+        bitField0_ = (bitField0_ & ~0x00000020);
+        description_ = "";
+        bitField0_ = (bitField0_ & ~0x00000040);
+        if (timelineBuilder_ == null) {
+          timeline_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000080);
+        } else {
+          timelineBuilder_.clear();
+        }
+        return this;
+      }
+
+      public Builder clone() {
+        return create().mergeFrom(buildPartial());
+      }
+
+      public com.google.protobuf.Descriptors.Descriptor
+          getDescriptorForType() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.internal_static_Span_descriptor;
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.Span getDefaultInstanceForType() {
+        return org.apache.htrace.protobuf.generated.SpanProtos.Span.getDefaultInstance();
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.Span build() {
+        org.apache.htrace.protobuf.generated.SpanProtos.Span result = buildPartial();
+        if (!result.isInitialized()) {
+          throw newUninitializedMessageException(result);
+        }
+        return result;
+      }
+
+      public org.apache.htrace.protobuf.generated.SpanProtos.Span buildPartial() {
+        org.apache.htrace.protobuf.generated.SpanProtos.Span result = new org.apache.htrace.protobuf.generated.SpanProtos.Span(this);
+        int from_bitField0_ = bitField0_;
+        int to_bitField0_ = 0;
+        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
+          to_bitField0_ |= 0x00000001;
+        }
+        result.traceId_ = traceId_;
+        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
+          to_bitField0_ |= 0x00000002;
+        }
+        result.parentId_ = parentId_;
+        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
+          to_bitField0_ |= 0x00000004;
+        }
+        result.start_ = start_;
+        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+          to_bitField0_ |= 0x00000008;
+        }
+        result.stop_ = stop_;
+        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {
+          to_bitField0_ |= 0x00000010;
+        }
+        result.spanId_ = spanId_;
+        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
+          to_bitField0_ |= 0x00000020;
+        }
+        result.processId_ = processId_;
+        if (((from_bitField0_ & 0x00000040) == 0x00000040)) {
+          to_bitField0_ |= 0x00000040;
+        }
+        result.description_ = description_;
+        if (timelineBuilder_ == null) {
+          if (((bitField0_ & 0x00000080) == 0x00000080)) {
+            timeline_ = java.util.Collections.unmodifiableList(timeline_);
+            bitField0_ = (bitField0_ & ~0x00000080);
+          }
+          result.timeline_ = timeline_;
+        } else {
+          result.timeline_ = timelineBuilder_.build();
+        }
+        result.bitField0_ = to_bitField0_;
+        onBuilt();
+        return result;
+      }
+
+      public Builder mergeFrom(com.google.protobuf.Message other) {
+        if (other instanceof org.apache.htrace.protobuf.generated.SpanProtos.Span) {
+          return mergeFrom((org.apache.htrace.protobuf.generated.SpanProtos.Span)other);
+        } else {
+          super.mergeFrom(other);
+          return this;
+        }
+      }
+
+      public Builder mergeFrom(org.apache.htrace.protobuf.generated.SpanProtos.Span other) {
+        if (other == org.apache.htrace.protobuf.generated.SpanProtos.Span.getDefaultInstance()) return this;
+        if (other.hasTraceId()) {
+          setTraceId(other.getTraceId());
+        }
+        if (other.hasParentId()) {
+          setParentId(other.getParentId());
+        }
+        if (other.hasStart()) {
+          setStart(other.getStart());
+        }
+        if (other.hasStop()) {
+          setStop(other.getStop());
+        }
+        if (other.hasSpanId()) {
+          setSpanId(other.getSpanId());
+        }
+        if (other.hasProcessId()) {
+          bitField0_ |= 0x00000020;
+          processId_ = other.processId_;
+          onChanged();
+        }
+        if (other.hasDescription()) {
+          bitField0_ |= 0x00000040;
+          description_ = other.description_;
+          onChanged();
+        }
+        if (timelineBuilder_ == null) {
+          if (!other.timeline_.isEmpty()) {
+            if (timeline_.isEmpty()) {
+              timeline_ = other.timeline_;
+              bitField0_ = (bitField0_ & ~0x00000080);
+            } else {
+              ensureTimelineIsMutable();
+              timeline_.addAll(other.timeline_);
+            }
+            onChanged();
+          }
+        } else {
+          if (!other.timeline_.isEmpty()) {
+            if (timelineBuilder_.isEmpty()) {
+              timelineBuilder_.dispose();
+              timelineBuilder_ = null;
+              timeline_ = other.timeline_;
+              bitField0_ = (bitField0_ & ~0x00000080);
+              timelineBuilder_ = 
+                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
+                   getTimelineFieldBuilder() : null;
+            } else {
+              timelineBuilder_.addAllMessages(other.timeline_);
+            }
+          }
+        }
+        this.mergeUnknownFields(other.getUnknownFields());
+        return this;
+      }
+
+      public final boolean isInitialized() {
+        if (!hasTraceId()) {
+          
+          return false;
+        }
+        if (!hasParentId()) {
+          
+          return false;
+        }
+        if (!hasStart()) {
+          
+          return false;
+        }
+        if (!hasStop()) {
+          
+          return false;
+        }
+        if (!hasSpanId()) {
+          
+          return false;
+        }
+        if (!hasProcessId()) {
+          
+          return false;
+        }
+        if (!hasDescription()) {
+          
+          return false;
+        }
+        for (int i = 0; i < getTimelineCount(); i++) {
+          if (!getTimeline(i).isInitialized()) {
+            
+            return false;
+          }
+        }
+        return true;
+      }
+
+      public Builder mergeFrom(
+          com.google.protobuf.CodedInputStream input,
+          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+          throws java.io.IOException {
+        org.apache.htrace.protobuf.generated.SpanProtos.Span parsedMessage = null;
+        try {
+          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+          parsedMessage = (org.apache.htrace.protobuf.generated.SpanProtos.Span) e.getUnfinishedMessage();
+          throw e;
+        } finally {
+          if (parsedMessage != null) {
+            mergeFrom(parsedMessage);
+          }
+        }
+        return this;
+      }
+      private int bitField0_;
+
+      // required int64 trace_id = 1;
+      private long traceId_ ;
+      /**
+       * <code>required int64 trace_id = 1;</code>
+       */
+      public boolean hasTraceId() {
+        return ((bitField0_ & 0x00000001) == 0x00000001);
+      }
+      /**
+       * <code>required int64 trace_id = 1;</code>
+       */
+      public long getTraceId() {
+        return traceId_;
+      }
+      /**
+       * <code>required int64 trace_id = 1;</code>
+       */
+      public Builder setTraceId(long value) {
+        bitField0_ |= 0x00000001;
+        traceId_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 trace_id = 1;</code>
+       */
+      public Builder clearTraceId() {
+        bitField0_ = (bitField0_ & ~0x00000001);
+        traceId_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required int64 parent_id = 2;
+      private long parentId_ ;
+      /**
+       * <code>required int64 parent_id = 2;</code>
+       */
+      public boolean hasParentId() {
+        return ((bitField0_ & 0x00000002) == 0x00000002);
+      }
+      /**
+       * <code>required int64 parent_id = 2;</code>
+       */
+      public long getParentId() {
+        return parentId_;
+      }
+      /**
+       * <code>required int64 parent_id = 2;</code>
+       */
+      public Builder setParentId(long value) {
+        bitField0_ |= 0x00000002;
+        parentId_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 parent_id = 2;</code>
+       */
+      public Builder clearParentId() {
+        bitField0_ = (bitField0_ & ~0x00000002);
+        parentId_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required int64 start = 3;
+      private long start_ ;
+      /**
+       * <code>required int64 start = 3;</code>
+       */
+      public boolean hasStart() {
+        return ((bitField0_ & 0x00000004) == 0x00000004);
+      }
+      /**
+       * <code>required int64 start = 3;</code>
+       */
+      public long getStart() {
+        return start_;
+      }
+      /**
+       * <code>required int64 start = 3;</code>
+       */
+      public Builder setStart(long value) {
+        bitField0_ |= 0x00000004;
+        start_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 start = 3;</code>
+       */
+      public Builder clearStart() {
+        bitField0_ = (bitField0_ & ~0x00000004);
+        start_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required int64 stop = 4;
+      private long stop_ ;
+      /**
+       * <code>required int64 stop = 4;</code>
+       */
+      public boolean hasStop() {
+        return ((bitField0_ & 0x00000008) == 0x00000008);
+      }
+      /**
+       * <code>required int64 stop = 4;</code>
+       */
+      public long getStop() {
+        return stop_;
+      }
+      /**
+       * <code>required int64 stop = 4;</code>
+       */
+      public Builder setStop(long value) {
+        bitField0_ |= 0x00000008;
+        stop_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 stop = 4;</code>
+       */
+      public Builder clearStop() {
+        bitField0_ = (bitField0_ & ~0x00000008);
+        stop_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required int64 span_id = 5;
+      private long spanId_ ;
+      /**
+       * <code>required int64 span_id = 5;</code>
+       */
+      public boolean hasSpanId() {
+        return ((bitField0_ & 0x00000010) == 0x00000010);
+      }
+      /**
+       * <code>required int64 span_id = 5;</code>
+       */
+      public long getSpanId() {
+        return spanId_;
+      }
+      /**
+       * <code>required int64 span_id = 5;</code>
+       */
+      public Builder setSpanId(long value) {
+        bitField0_ |= 0x00000010;
+        spanId_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required int64 span_id = 5;</code>
+       */
+      public Builder clearSpanId() {
+        bitField0_ = (bitField0_ & ~0x00000010);
+        spanId_ = 0L;
+        onChanged();
+        return this;
+      }
+
+      // required string process_id = 6;
+      private java.lang.Object processId_ = "";
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public boolean hasProcessId() {
+        return ((bitField0_ & 0x00000020) == 0x00000020);
+      }
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public java.lang.String getProcessId() {
+        java.lang.Object ref = processId_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          processId_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public com.google.protobuf.ByteString
+          getProcessIdBytes() {
+        java.lang.Object ref = processId_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          processId_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public Builder setProcessId(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000020;
+        processId_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public Builder clearProcessId() {
+        bitField0_ = (bitField0_ & ~0x00000020);
+        processId_ = getDefaultInstance().getProcessId();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string process_id = 6;</code>
+       */
+      public Builder setProcessIdBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000020;
+        processId_ = value;
+        onChanged();
+        return this;
+      }
+
+      // required string description = 7;
+      private java.lang.Object description_ = "";
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public boolean hasDescription() {
+        return ((bitField0_ & 0x00000040) == 0x00000040);
+      }
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public java.lang.String getDescription() {
+        java.lang.Object ref = description_;
+        if (!(ref instanceof java.lang.String)) {
+          java.lang.String s = ((com.google.protobuf.ByteString) ref)
+              .toStringUtf8();
+          description_ = s;
+          return s;
+        } else {
+          return (java.lang.String) ref;
+        }
+      }
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public com.google.protobuf.ByteString
+          getDescriptionBytes() {
+        java.lang.Object ref = description_;
+        if (ref instanceof String) {
+          com.google.protobuf.ByteString b = 
+              com.google.protobuf.ByteString.copyFromUtf8(
+                  (java.lang.String) ref);
+          description_ = b;
+          return b;
+        } else {
+          return (com.google.protobuf.ByteString) ref;
+        }
+      }
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public Builder setDescription(
+          java.lang.String value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000040;
+        description_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public Builder clearDescription() {
+        bitField0_ = (bitField0_ & ~0x00000040);
+        description_ = getDefaultInstance().getDescription();
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>required string description = 7;</code>
+       */
+      public Builder setDescriptionBytes(
+          com.google.protobuf.ByteString value) {
+        if (value == null) {
+    throw new NullPointerException();
+  }
+  bitField0_ |= 0x00000040;
+        description_ = value;
+        onChanged();
+        return this;
+      }
+
+      // repeated .TimelineAnnotation timeline = 8;
+      private java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> timeline_ =
+        java.util.Collections.emptyList();
+      private void ensureTimelineIsMutable() {
+        if (!((bitField0_ & 0x00000080) == 0x00000080)) {
+          timeline_ = new java.util.ArrayList<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation>(timeline_);
+          bitField0_ |= 0x00000080;
+         }
+      }
+
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder> timelineBuilder_;
+
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> getTimelineList() {
+        if (timelineBuilder_ == null) {
+          return java.util.Collections.unmodifiableList(timeline_);
+        } else {
+          return timelineBuilder_.getMessageList();
+        }
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public int getTimelineCount() {
+        if (timelineBuilder_ == null) {
+          return timeline_.size();
+        } else {
+          return timelineBuilder_.getCount();
+        }
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation getTimeline(int index) {
+        if (timelineBuilder_ == null) {
+          return timeline_.get(index);
+        } else {
+          return timelineBuilder_.getMessage(index);
+        }
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder setTimeline(
+          int index, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation value) {
+        if (timelineBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTimelineIsMutable();
+          timeline_.set(index, value);
+          onChanged();
+        } else {
+          timelineBuilder_.setMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder setTimeline(
+          int index, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder builderForValue) {
+        if (timelineBuilder_ == null) {
+          ensureTimelineIsMutable();
+          timeline_.set(index, builderForValue.build());
+          onChanged();
+        } else {
+          timelineBuilder_.setMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder addTimeline(org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation value) {
+        if (timelineBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTimelineIsMutable();
+          timeline_.add(value);
+          onChanged();
+        } else {
+          timelineBuilder_.addMessage(value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder addTimeline(
+          int index, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation value) {
+        if (timelineBuilder_ == null) {
+          if (value == null) {
+            throw new NullPointerException();
+          }
+          ensureTimelineIsMutable();
+          timeline_.add(index, value);
+          onChanged();
+        } else {
+          timelineBuilder_.addMessage(index, value);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder addTimeline(
+          org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder builderForValue) {
+        if (timelineBuilder_ == null) {
+          ensureTimelineIsMutable();
+          timeline_.add(builderForValue.build());
+          onChanged();
+        } else {
+          timelineBuilder_.addMessage(builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder addTimeline(
+          int index, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder builderForValue) {
+        if (timelineBuilder_ == null) {
+          ensureTimelineIsMutable();
+          timeline_.add(index, builderForValue.build());
+          onChanged();
+        } else {
+          timelineBuilder_.addMessage(index, builderForValue.build());
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder addAllTimeline(
+          java.lang.Iterable<? extends org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation> values) {
+        if (timelineBuilder_ == null) {
+          ensureTimelineIsMutable();
+          super.addAll(values, timeline_);
+          onChanged();
+        } else {
+          timelineBuilder_.addAllMessages(values);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder clearTimeline() {
+        if (timelineBuilder_ == null) {
+          timeline_ = java.util.Collections.emptyList();
+          bitField0_ = (bitField0_ & ~0x00000080);
+          onChanged();
+        } else {
+          timelineBuilder_.clear();
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public Builder removeTimeline(int index) {
+        if (timelineBuilder_ == null) {
+          ensureTimelineIsMutable();
+          timeline_.remove(index);
+          onChanged();
+        } else {
+          timelineBuilder_.remove(index);
+        }
+        return this;
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder getTimelineBuilder(
+          int index) {
+        return getTimelineFieldBuilder().getBuilder(index);
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder getTimelineOrBuilder(
+          int index) {
+        if (timelineBuilder_ == null) {
+          return timeline_.get(index);  } else {
+          return timelineBuilder_.getMessageOrBuilder(index);
+        }
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public java.util.List<? extends org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder> 
+           getTimelineOrBuilderList() {
+        if (timelineBuilder_ != null) {
+          return timelineBuilder_.getMessageOrBuilderList();
+        } else {
+          return java.util.Collections.unmodifiableList(timeline_);
+        }
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder addTimelineBuilder() {
+        return getTimelineFieldBuilder().addBuilder(
+            org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder addTimelineBuilder(
+          int index) {
+        return getTimelineFieldBuilder().addBuilder(
+            index, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.getDefaultInstance());
+      }
+      /**
+       * <code>repeated .TimelineAnnotation timeline = 8;</code>
+       */
+      public java.util.List<org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder> 
+           getTimelineBuilderList() {
+        return getTimelineFieldBuilder().getBuilderList();
+      }
+      private com.google.protobuf.RepeatedFieldBuilder<
+          org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder> 
+          getTimelineFieldBuilder() {
+        if (timelineBuilder_ == null) {
+          timelineBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
+              org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation.Builder, org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotationOrBuilder>(
+                  timeline_,
+                  ((bitField0_ & 0x00000080) == 0x00000080),
+                  getParentForChildren(),
+                  isClean());
+          timeline_ = null;
+        }
+        return timelineBuilder_;
+      }
+
+      // @@protoc_insertion_point(builder_scope:Span)
+    }
+
+    static {
+      defaultInstance = new Span(true);
+      defaultInstance.initFields();
+    }
+
+    // @@protoc_insertion_point(class_scope:Span)
+  }
+
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_TimelineAnnotation_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_TimelineAnnotation_fieldAccessorTable;
+  private static com.google.protobuf.Descriptors.Descriptor
+    internal_static_Span_descriptor;
+  private static
+    com.google.protobuf.GeneratedMessage.FieldAccessorTable
+      internal_static_Span_fieldAccessorTable;
+
+  public static com.google.protobuf.Descriptors.FileDescriptor
+      getDescriptor() {
+    return descriptor;
+  }
+  private static com.google.protobuf.Descriptors.FileDescriptor
+      descriptor;
+  static {
+    java.lang.String[] descriptorData = {
+      "\n\nSpan.proto\"3\n\022TimelineAnnotation\022\014\n\004ti" +
+      "me\030\001 \002(\003\022\017\n\007message\030\002 \002(\t\"\251\001\n\004Span\022\020\n\010tr" +
+      "ace_id\030\001 \002(\003\022\021\n\tparent_id\030\002 \002(\003\022\r\n\005start" +
+      "\030\003 \002(\003\022\014\n\004stop\030\004 \002(\003\022\017\n\007span_id\030\005 \002(\003\022\022\n" +
+      "\nprocess_id\030\006 \002(\t\022\023\n\013description\030\007 \002(\t\022%" +
+      "\n\010timeline\030\010 \003(\0132\023.TimelineAnnotationB0\n" +
+      "\035org.htrace.protobuf.generatedB\nSpanProt" +
+      "osH\001\240\001\001"
+    };
+    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
+      new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
+        public com.google.protobuf.ExtensionRegistry assignDescriptors(
+            com.google.protobuf.Descriptors.FileDescriptor root) {
+          descriptor = root;
+          internal_static_TimelineAnnotation_descriptor =
+            getDescriptor().getMessageTypes().get(0);
+          internal_static_TimelineAnnotation_fieldAccessorTable = new
+            com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+              internal_static_TimelineAnnotation_descriptor,
+              new java.lang.String[] { "Time", "Message", });
+          internal_static_Span_descriptor =
+            getDescriptor().getMessageTypes().get(1);
+          internal_static_Span_fieldAccessorTable = new
+            com.google.protobuf.GeneratedMessage.FieldAccessorTable(
+              internal_static_Span_descriptor,
+              new java.lang.String[] { "TraceId", "ParentId", "Start", "Stop", "SpanId", "ProcessId", "Description", "Timeline", });
+          return null;
+        }
+      };
+    com.google.protobuf.Descriptors.FileDescriptor
+      .internalBuildGeneratedFileFrom(descriptorData,
+        new com.google.protobuf.Descriptors.FileDescriptor[] {
+        }, assigner);
+  }
+
+  // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewer.java b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewer.java
new file mode 100644
index 0000000..114ab4f
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewer.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.viewer;
+
+import com.google.protobuf.Message;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HConnection;
+import org.apache.hadoop.hbase.client.HConnectionManager;
+import org.apache.hadoop.hbase.client.HTableInterface;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.htrace.impl.HBaseSpanReceiver;
+import org.apache.htrace.protobuf.generated.SpanProtos;
+
+public class HBaseSpanViewer {
+  private static final Log LOG = LogFactory.getLog(HBaseSpanViewer.class);
+  private Configuration conf;
+  private HConnection hconnection;
+  private HTableInterface htable;
+  private byte[] table;
+  private byte[] cf; 
+  private byte[] icf; 
+
+  public HBaseSpanViewer(Configuration conf) {
+    this.conf = conf;
+    this.table = Bytes.toBytes(conf.get(HBaseSpanReceiver.TABLE_KEY,
+                                        HBaseSpanReceiver.DEFAULT_TABLE));
+    this.cf = Bytes.toBytes(conf.get(HBaseSpanReceiver.COLUMNFAMILY_KEY,
+                                     HBaseSpanReceiver.DEFAULT_COLUMNFAMILY));
+    this.icf = Bytes.toBytes(conf.get(HBaseSpanReceiver.INDEXFAMILY_KEY,
+                                      HBaseSpanReceiver.DEFAULT_INDEXFAMILY));
+  }
+
+  public void close() {
+    stopClient();
+  }
+
+  public void startClient() {
+    if (this.htable == null) {
+      try {
+        this.hconnection = HConnectionManager.createConnection(conf);
+        this.htable = hconnection.getTable(table);
+      } catch (IOException e) {
+        LOG.warn("Failed to create HBase connection. " + e.getMessage());
+      }
+    }
+  }
+
+  public void stopClient() {
+    try {
+      if (this.htable != null) {
+        this.htable.close();
+        this.htable = null;
+      }
+      if (this.hconnection != null) {
+        this.hconnection.close();
+        this.hconnection = null;
+      }
+    } catch (IOException e) {
+      LOG.warn("Failed to close HBase connection. " + e.getMessage());
+    }
+  }
+
+  public List<SpanProtos.Span> getSpans(long traceid) throws IOException {
+    startClient();
+    List<SpanProtos.Span> spans = new ArrayList<SpanProtos.Span>();
+    Get get = new Get(Bytes.toBytes(traceid));
+    get.addFamily(this.cf);
+    try {
+      for (Cell cell : htable.get(get).listCells()) {
+        InputStream in = new ByteArrayInputStream(cell.getQualifierArray(),
+                                                  cell.getQualifierOffset(),
+                                                  cell.getQualifierLength());
+        spans.add(SpanProtos.Span.parseFrom(in));
+      }
+    } catch (IOException e) {
+      LOG.warn("Failed to get spans from HBase. " + e.getMessage());
+      stopClient();
+    }
+    return spans;
+  }
+
+  public List<SpanProtos.Span> getRootSpans() throws IOException {
+    startClient();
+    Scan scan = new Scan();
+    scan.addColumn(this.icf, HBaseSpanReceiver.INDEX_SPAN_QUAL);
+    List<SpanProtos.Span> spans = new ArrayList<SpanProtos.Span>();
+    try {
+      ResultScanner scanner = htable.getScanner(scan);
+      Result result = null;
+      while ((result = scanner.next()) != null) {
+        for (Cell cell : result.listCells()) {
+          InputStream in = new ByteArrayInputStream(cell.getValueArray(),
+                                                    cell.getValueOffset(),
+                                                    cell.getValueLength());
+          spans.add(SpanProtos.Span.parseFrom(in));
+        }
+      }
+    } catch (IOException e) {
+      LOG.warn("Failed to get root spans from HBase. " + e.getMessage());
+      stopClient();
+    }
+    return spans;
+  }
+
+  public static String toJsonString(final Message message) throws IOException {
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    OutputStreamWriter writer =
+        new OutputStreamWriter(out, Charset.defaultCharset());
+    appendJsonString(message, writer);
+    writer.flush();
+    out.flush();
+    return out.toString();
+  }
+
+  public static void appendJsonString(final Message message,
+                                        OutputStreamWriter writer) throws IOException {
+    writer.append("{");
+    for (Iterator<Map.Entry<FieldDescriptor, Object>> iter =
+           message.getAllFields().entrySet().iterator(); iter.hasNext();) {
+      Map.Entry<FieldDescriptor, Object> field = iter.next();
+      appendFields(field.getKey(), field.getValue(), writer);
+      if (iter.hasNext()) {
+        writer.append(",");
+      }
+    }
+    writer.append("}");
+  }
+  
+
+  private static void appendFields(FieldDescriptor fd,
+                                   Object value, 
+                                   OutputStreamWriter writer) throws IOException {
+    writer.append("\"");
+    writer.append(fd.getName());
+    writer.append("\"");
+    writer.append(":");
+    if (fd.isRepeated()) {
+      writer.append("[");
+      for (Iterator<?> it = ((List<?>) value).iterator(); it.hasNext();) {
+        appendValue(fd, it.next(), writer);
+        if (it.hasNext()) {
+          writer.append(",");
+        }
+      }
+      writer.append("]");
+    } else {
+      appendValue(fd, value, writer);
+    }
+  }
+
+  private static void appendValue(FieldDescriptor fd,
+                                  Object value, 
+                                  OutputStreamWriter writer) throws IOException {
+    switch (fd.getType()) {
+    case INT64: // write int as string for handling in javascript
+    case STRING:
+      writer.append("\"");
+      writer.append(value.toString());
+      writer.append("\"");
+      break;
+    case MESSAGE:
+      appendJsonString((Message)value, writer);
+      break;
+    default:
+      throw new IOException("unexpected field type.");
+    }
+  }
+
+  /**
+   * Run basic test.
+   * @throws IOException
+   */
+  public static void main(String[] args) throws IOException {
+    HBaseSpanViewer viewer = new HBaseSpanViewer(HBaseConfiguration.create());
+    if (args.length == 0) {
+      List<SpanProtos.Span> spans = viewer.getRootSpans();
+      for (SpanProtos.Span span : spans) {
+        System.out.println(toJsonString(span));
+      }
+    } else {
+      List<SpanProtos.Span> spans = viewer.getSpans(Long.parseLong(args[0]));
+      for (SpanProtos.Span span : spans) {
+        System.out.println(toJsonString(span));
+      }
+    }
+    viewer.close();
+  }
+}
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerServer.java b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerServer.java
new file mode 100644
index 0000000..da90008
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerServer.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.viewer;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.http.HttpServer2;
+import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.util.Tool;
+import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+
+public class HBaseSpanViewerServer implements Tool {
+  private static final Log LOG = LogFactory.getLog(HBaseSpanViewerServer.class);
+  public static final String HTRACE_VIEWER_HTTP_ADDRESS_KEY = "htrace.viewer.http.address";
+  public static final String HTRACE_VIEWER_HTTP_ADDRESS_DEFAULT = "0.0.0.0:16900";
+  public static final String HTRACE_CONF_ATTR = "htrace.conf";
+  public static final String HTRACE_APPDIR = "webapps";
+  public static final String NAME = "htrace";
+
+  private Configuration conf;
+  private HttpServer2 httpServer;
+  private InetSocketAddress httpAddress;
+
+  public void setConf(Configuration conf) {
+    this.conf = conf;
+  }
+
+  public Configuration getConf() {
+    return this.conf;
+  }
+
+  void start() throws IOException {
+    httpAddress = NetUtils.createSocketAddr(
+        conf.get(HTRACE_VIEWER_HTTP_ADDRESS_KEY, HTRACE_VIEWER_HTTP_ADDRESS_DEFAULT));
+    conf.set(HTRACE_VIEWER_HTTP_ADDRESS_KEY, NetUtils.getHostPortString(httpAddress));
+    HttpServer2.Builder builder = new HttpServer2.Builder();
+    builder.setName(NAME).setConf(conf);
+    if (httpAddress.getPort() == 0) {
+      builder.setFindPort(true);
+    }
+    URI uri = URI.create("http://" + NetUtils.getHostPortString(httpAddress));
+    builder.addEndpoint(uri);
+    LOG.info("Starting Web-server for " + NAME + " at: " + uri);
+    httpServer = builder.build();
+    httpServer.setAttribute(HTRACE_CONF_ATTR, conf);
+    httpServer.addServlet("gettraces",
+                          HBaseSpanViewerTracesServlet.PREFIX,
+                          HBaseSpanViewerTracesServlet.class);
+    httpServer.addServlet("getspans",
+                          HBaseSpanViewerSpansServlet.PREFIX + "/*",
+                          HBaseSpanViewerSpansServlet.class);
+
+    // for webapps/htrace bundled in jar.
+    String rb = httpServer.getClass()
+                          .getClassLoader()
+                          .getResource("webapps/" + NAME)
+                          .toString();
+    httpServer.getWebAppContext().setResourceBase(rb);
+
+    httpServer.start();
+    httpAddress = httpServer.getConnectorAddress(0);
+  }
+
+  void join() throws Exception {
+    if (httpServer != null) {
+      httpServer.join();
+    }
+  }
+
+  void stop() throws Exception {
+    if (httpServer != null) {
+      httpServer.stop();
+    }
+  }
+
+  InetSocketAddress getHttpAddress() {
+    return httpAddress;
+  }
+
+  public int run(String[] args) throws Exception {
+    start();
+    join();
+    stop();
+    return 0;
+  }
+
+  /**
+   * @throws IOException
+   */
+  public static void main(String[] args) throws Exception {
+    ToolRunner.run(HBaseConfiguration.create(), new HBaseSpanViewerServer(), args);
+  }
+}
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerSpansServlet.java b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerSpansServlet.java
new file mode 100644
index 0000000..8f3f50f
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerSpansServlet.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.viewer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.util.ServletUtil;
+import org.apache.htrace.protobuf.generated.SpanProtos;
+
+public class HBaseSpanViewerSpansServlet extends HttpServlet {
+  private static final Log LOG = LogFactory.getLog(HBaseSpanViewerSpansServlet.class);
+  public static final String PREFIX = "/getspans";
+  private static final ThreadLocal<HBaseSpanViewer> tlviewer =
+      new ThreadLocal<HBaseSpanViewer>() {
+        @Override
+        protected HBaseSpanViewer initialValue() {
+          return null;
+        }
+      };
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void doGet(HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    final String path =
+        validatePath(ServletUtil.getDecodedPath(request, PREFIX));
+    if (path == null) {
+      response.setContentType("text/plain");
+      response.getWriter().print("Invalid input");
+      return;
+    }
+    HBaseSpanViewer viewer = tlviewer.get();
+    if (viewer == null) {
+      final Configuration conf = (Configuration) getServletContext()
+        .getAttribute(HBaseSpanViewerServer.HTRACE_CONF_ATTR);
+      viewer = new HBaseSpanViewer(conf);
+      tlviewer.set(viewer);
+    }
+    Long traceid = Long.parseLong(path.substring(1));
+    response.setContentType("application/javascript");
+    PrintWriter out = response.getWriter();
+    out.print("[");
+    boolean first = true;
+    for (SpanProtos.Span span : viewer.getSpans(traceid)) {
+      if (first) {
+        first = false;
+      } else {
+        out.print(",");
+      }
+      out.print(HBaseSpanViewer.toJsonString(span));
+    }
+    out.print("]");
+  }
+
+  @Override
+  public void init() throws ServletException {
+  }
+
+  @Override
+  public void destroy() {
+    HBaseSpanViewer viewer = tlviewer.get();
+    if (viewer != null) {
+      viewer.close();
+    }
+  }
+
+  public static String validatePath(String p) {
+    return p == null || p.length() == 0?
+      null: new Path(p).toUri().getPath();
+  }
+}
diff --git a/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerTracesServlet.java b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerTracesServlet.java
new file mode 100644
index 0000000..b0370c4
--- /dev/null
+++ b/htrace-hbase/src/main/java/org/apache/htrace/viewer/HBaseSpanViewerTracesServlet.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.viewer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.util.ServletUtil;
+import org.apache.htrace.protobuf.generated.SpanProtos;
+
+public class HBaseSpanViewerTracesServlet extends HttpServlet {
+  private static final Log LOG = LogFactory.getLog(HBaseSpanViewerTracesServlet.class);
+  public static final String PREFIX = "/gettraces";
+  private static final ThreadLocal<HBaseSpanViewer> tlviewer =
+      new ThreadLocal<HBaseSpanViewer>() {
+        @Override
+        protected HBaseSpanViewer initialValue() {
+          return null;
+        }
+      };
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void doGet(HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    HBaseSpanViewer viewer = tlviewer.get();
+    if (viewer == null) {
+      final Configuration conf = (Configuration) getServletContext()
+        .getAttribute(HBaseSpanViewerServer.HTRACE_CONF_ATTR);
+      viewer = new HBaseSpanViewer(conf);
+      tlviewer.set(viewer);
+    }
+    response.setContentType("application/javascript");
+    PrintWriter out = response.getWriter();
+    out.print("[");
+    boolean first = true;
+    for (SpanProtos.Span span : viewer.getRootSpans()) {
+      if (first) {
+        first = false;
+      } else {
+        out.print(",");
+      }
+      out.print(HBaseSpanViewer.toJsonString(span));
+    }
+    out.print("]");
+  }
+
+  @Override
+  public void init() throws ServletException {
+  }
+
+  @Override
+  public void destroy() {
+    HBaseSpanViewer viewer = tlviewer.get();
+    if (viewer != null) {
+      viewer.close();
+    }
+  }
+}
diff --git a/htrace-hbase/src/main/protobuf/Span.proto b/htrace-hbase/src/main/protobuf/Span.proto
new file mode 100644
index 0000000..e8dc369
--- /dev/null
+++ b/htrace-hbase/src/main/protobuf/Span.proto
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+option java_package = "org.apache.htrace.protobuf.generated";
+option java_outer_classname = "SpanProtos";
+option java_generate_equals_and_hash = true;
+option optimize_for = SPEED;
+
+message TimelineAnnotation {
+  required int64 time = 1;
+  required string message = 2;
+}
+
+message Span {
+  required int64 trace_id = 1;
+  required int64 parent_id = 2;
+  required int64 start = 3;
+  required int64 stop = 4;
+  required int64 span_id = 5;
+  required string process_id = 6;
+  required string description = 7;
+  repeated TimelineAnnotation timeline = 8;
+}
diff --git a/htrace-hbase/src/main/webapps/htrace/WEB-INF/web.xml b/htrace-hbase/src/main/webapps/htrace/WEB-INF/web.xml
new file mode 100644
index 0000000..9d0ae0d
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/WEB-INF/web.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
+</web-app>
diff --git a/htrace-hbase/src/main/webapps/htrace/index.html b/htrace-hbase/src/main/webapps/htrace/index.html
new file mode 100644
index 0000000..7bbd6bc
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/index.html
@@ -0,0 +1,24 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="REFRESH" content="0;url=traces.html" />
+<title>HTrace Viewer</title>
+</head>
+</html>
diff --git a/htrace-hbase/src/main/webapps/htrace/spans.html b/htrace-hbase/src/main/webapps/htrace/spans.html
new file mode 100644
index 0000000..28b48b7
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/spans.html
@@ -0,0 +1,28 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<script src="/static/d3.min.js" type="text/javascript"></script>
+<script src="spans.js" type="text/javascript"></script>
+<head>
+  <link href="/static/bootstrap.min.css" rel="stylesheet">
+  <link href="/static/bootstrap-theme.min.css" rel="stylesheet">
+</head>
+<body>
+</body>
+</html>
diff --git a/htrace-hbase/src/main/webapps/htrace/spans.js b/htrace-hbase/src/main/webapps/htrace/spans.js
new file mode 100644
index 0000000..03ff6fb
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/spans.js
@@ -0,0 +1,166 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const height_span = 20;
+const width_span = 700;
+const size_tl = 6;
+const margin = {top: 50, bottom: 50, left: 50, right: 1000, process: 250};
+
+const ROOT_SPAN_ID = "477902"; // constants defined in org.apache.htrace.Span
+const traceid = window.location.search.substring(1).split("=")[1];
+
+d3.json("/getspans/" + traceid, function(spans) {
+    spans.map(function(s) {
+        s.start = parseInt(s.start);
+        s.stop = parseInt(s.stop);
+        if (s.timeline) {
+          s.timeline.forEach(function(t) { t.time = parseInt(t.time); });
+        }
+      });
+    var byparent = d3.nest()
+      .sortValues(function(a, b) {
+          return a.start < b.start ? -1 : a.start > b.start ? 1 : 0;
+        })
+      .key(function(e) { return e.parent_id; })
+      .map(spans, d3.map);
+    addchildren(byparent.get(ROOT_SPAN_ID), byparent);
+    var sortedspans = [];
+    traverse(0, byparent.get(ROOT_SPAN_ID), function(e) { sortedspans.push(e); });
+
+    var height_screen = spans.length * height_span;
+    var tmin = d3.min(spans, function(s) { return s.start; });
+    var tmax = d3.max(spans, function(s) { return s.stop; });
+    var xscale = d3.time.scale()
+      .domain([new Date(tmin), new Date(tmax)]).range([0, width_span]);
+
+    var svg = d3.select("body").append("svg")
+      .attr("width", width_span + margin.process + margin.left + margin.right)
+      .attr("height", height_screen + margin.top + margin.bottom)
+      .append("g")
+      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+    var bars = svg.append("g")
+      .attr("id", "bars")
+      .attr("width", width_span)
+      .attr("height", height_screen)
+      .attr("transform", "translate(" + margin.process + ", 0)");
+    
+    var span_g = bars.selectAll("g.span")
+      .data(sortedspans)
+      .enter()
+      .append("g")
+      .attr("transform", function(s, i) {
+          return "translate(0, " + (i * height_span + 5) + ")";
+        })
+      .classed("timeline", function(d) { return d.timeline; });
+
+    span_g.append("text")
+      .text(function(s) { return s.process_id; })
+      .style("alignment-baseline", "hanging")
+      .attr("transform", function(s) {
+          return "translate(" + (s.depth * 10 - margin.process) + ", 0)";
+        });
+
+    var rect_g = span_g.append("g")
+      .attr("transform", function(s) {
+          return "translate(" + xscale(new Date(s.start)) + ", 0)";
+        });
+
+    rect_g.append("rect")
+      .attr("height", height_span - 1)
+      .attr("width", function (s) {
+          return (width_span * (s.stop - s.start)) / (tmax - tmin) + 1;
+        })
+      .style("fill", "lightblue")
+      .attr("class", "span")
+
+    rect_g.append("text")
+      .text(function(s){ return s.description; })
+      .style("alignment-baseline", "hanging");
+
+    rect_g.append("text")
+      .text(function(s){ return s.stop - s.start; })
+      .style("alignment-baseline", "baseline")
+      .style("text-anchor", "end")
+      .style("font-size", "10px")
+      .attr("transform", function(s, i) { return "translate(0, 10)"; });
+
+    bars.selectAll("g.timeline").selectAll("rect.timeline")
+      .data(function(s) { return s.timeline; })
+      .enter()
+      .append("rect")
+      .style("fill", "red")
+      .attr("height", size_tl)
+      .attr("width", size_tl)
+      .attr("transform", function(t) {
+          return "translate(" + xscale(t.time) + "," + (height_span - 1 - size_tl) + ")";
+        })
+      .classed("timeline");
+
+    var popup = d3.select("body").append("div")
+      .attr("class", "popup")
+      .style("opacity", 0);
+
+    bars.selectAll("g.timeline")
+      .on("mouseover", function(d) {
+          popup.transition().duration(300).style("opacity", .95);
+          var text = "<table>";
+          d.timeline.forEach(function (t) {
+              text += "<tr><td>" + (t.time - tmin) + "</td>";
+              text += "<td> : " + t.message + "<td/></tr>";
+            });
+          text += "</table>"
+          popup.html(text)
+            .style("left", (document.body.scrollLeft + 30) + "px")
+            .style("top", (document.body.scrollTop + 30) + "px")
+            .style("width", "700px")
+            .style("background", "orange")
+            .style("position", "absolute");
+        })
+      .on("mouseout", function(d) {
+          popup.transition().duration(300).style("opacity", 0);
+        });
+    
+    var axis = d3.svg.axis()
+      .scale(xscale)
+      .orient("top")
+      .tickValues(xscale.domain())
+      .tickFormat(d3.time.format("%x %X.%L"))
+      .tickSize(6, 3);
+
+    bars.append("g").attr("class", "axis").call(axis);
+  });
+
+function addchildren(nodes, byparent) {
+  nodes.forEach(function(e) {
+      if (byparent.get(e.span_id)) {
+        e.children = byparent.get(e.span_id);
+        addchildren(e.children, byparent);
+      }
+    });
+}
+
+function traverse(depth, children, func) {
+  children.forEach(function(e) {
+      e.depth = depth;
+      func(e);
+      if (e.children) {
+        traverse (depth + 1, e.children, func);
+      }
+    });
+}
diff --git a/htrace-hbase/src/main/webapps/htrace/traces.html b/htrace-hbase/src/main/webapps/htrace/traces.html
new file mode 100644
index 0000000..a8e1e2a
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/traces.html
@@ -0,0 +1,28 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<script src="/static/d3.min.js" type="text/javascript"></script>
+<script src="traces.js" type="text/javascript"></script>
+<head>
+  <link href="/static/bootstrap.min.css" rel="stylesheet">
+  <link href="/static/bootstrap-theme.min.css" rel="stylesheet">
+</head>
+<body>
+</body>
+</html>
diff --git a/htrace-hbase/src/main/webapps/htrace/traces.js b/htrace-hbase/src/main/webapps/htrace/traces.js
new file mode 100644
index 0000000..003f5dd
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/htrace/traces.js
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+d3.json("/gettraces", function(spans) {
+    spans.map(function(s) {
+        s.start = parseInt(s.start);
+        s.stop = parseInt(s.stop);
+      });
+    var table = d3.select("body")
+      .append("table")
+      .attr("style", "white-space: nowrap;")
+      .attr("class", "table table-condensed");
+    var thead = table.append("thead");    
+    var tbody = table.append("tbody");
+    var columns = ["start", "process_id", "description"];
+
+    thead.append("tr")
+      .selectAll("th")
+      .data(columns)
+      .enter()
+      .append("th")
+      .html(function(d) { return d; });
+
+    var rows = tbody.selectAll("tr")
+      .data(spans)
+      .enter()
+      .append("tr")
+      .sort(function(a, b) {
+          return a.start > b.start ? -1 : a.start < b.start ? 1 : 0;
+        })
+      .attr("onclick", function(d) {
+          return "window.location='spans.html?traceid=" + d.trace_id + "'";
+        });
+
+    rows.selectAll("td")
+      .data(function(s) {
+          return columns.map(function(c) { return s[c]; });
+        })
+      .enter()
+      .append("td")
+      .html(function(d) { return d; });
+
+    rows.select("td")
+      .html(function(d) {
+          return d3.time.format("%x %X.%L")(new Date(d.start));
+        });
+  });
diff --git a/htrace-hbase/src/main/webapps/static/bootstrap-theme.min.css b/htrace-hbase/src/main/webapps/static/bootstrap-theme.min.css
new file mode 100755
index 0000000..c31428b
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/static/bootstrap-theme.min.css
@@ -0,0 +1,10 @@
+/*!
+ * Bootstrap v3.0.0
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world by @mdo and @fat.
+ */
+.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-dan [...]
\ No newline at end of file
diff --git a/htrace-hbase/src/main/webapps/static/bootstrap.min.css b/htrace-hbase/src/main/webapps/static/bootstrap.min.css
new file mode 100755
index 0000000..a553c4f
--- /dev/null
+++ b/htrace-hbase/src/main/webapps/static/bootstrap.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Bootstrap v3.0.0
+ *
+ * Copyright 2013 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world by @mdo and @fat.
+ *//*! normalize.css v2.1.0 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}[hidden]{display:none}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:focus{outline:thin dotted}a:active,a:hover{outline:0}h1{margin:.67em 0;font-size:2em}abbr[title]{border-bottom:1px dotted}b [...]
\ No newline at end of file
diff --git a/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java b/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
new file mode 100644
index 0000000..8743f8a
--- /dev/null
+++ b/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.impl;
+
+import java.io.IOException;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.htrace.HBaseHTraceConfiguration;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.SpanReceiverBuilder;
+import org.junit.Assert;
+
+
+public class HBaseTestUtil {
+  public static Configuration configure(Configuration conf) {
+    Configuration hconf = HBaseConfiguration.create(conf);
+    hconf.set(HBaseHTraceConfiguration.KEY_PREFIX +
+              HBaseSpanReceiver.COLLECTOR_QUORUM_KEY,
+              conf.get(HConstants.ZOOKEEPER_QUORUM));
+    hconf.setInt(HBaseHTraceConfiguration.KEY_PREFIX +
+                 HBaseSpanReceiver.ZOOKEEPER_CLIENT_PORT_KEY,
+                 conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, 2181));
+    hconf.set(HBaseHTraceConfiguration.KEY_PREFIX +
+              HBaseSpanReceiver.ZOOKEEPER_ZNODE_PARENT_KEY,
+              conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT));
+    return hconf;
+  }
+
+  public static Table createTable(HBaseTestingUtility util) {
+    Table htable = null;
+    try {
+      htable = util.createTable(Bytes.toBytes(HBaseSpanReceiver.DEFAULT_TABLE),
+                                new byte[][]{Bytes.toBytes(HBaseSpanReceiver.DEFAULT_COLUMNFAMILY),
+                                             Bytes.toBytes(HBaseSpanReceiver.DEFAULT_INDEXFAMILY)});
+    } catch (IOException e) {
+      Assert.fail("failed to create htrace table. " + e.getMessage());
+    }
+    return htable;
+  }
+
+  public static SpanReceiver startReceiver(Configuration conf) {
+    return new SpanReceiverBuilder(new HBaseHTraceConfiguration(conf)).build();
+  }
+
+  public static SpanReceiver startReceiver(HBaseTestingUtility util) {
+    return startReceiver(configure(util.getConfiguration()));
+  }
+
+  public static void stopReceiver(SpanReceiver receiver) {
+    if (receiver != null) {
+      try {
+        receiver.close();
+        receiver = null;
+      } catch (IOException e) {
+        Assert.fail("failed to close span receiver. " + e.getMessage());
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
new file mode 100644
index 0000000..d3cffe2
--- /dev/null
+++ b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.TimelineAnnotation;
+import org.apache.htrace.TraceCreator;
+import org.apache.htrace.TraceTree.SpansByParent;
+import org.apache.htrace.TraceTree;
+import org.apache.htrace.protobuf.generated.SpanProtos;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.google.common.collect.Multimap;
+
+
+public class TestHBaseSpanReceiver {
+  private static final Log LOG = LogFactory.getLog(TestHBaseSpanReceiver.class);
+  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    UTIL.startMiniCluster(1);
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    UTIL.shutdownMiniCluster();
+  }
+
+  // Reenable after fix circular dependency
+  @Ignore @Test
+  public void testHBaseSpanReceiver() {
+    Table htable = HBaseTestUtil.createTable(UTIL);
+    SpanReceiver receiver = HBaseTestUtil.startReceiver(UTIL);
+    TraceCreator tc = new TraceCreator(receiver);
+    tc.createThreadedTrace();
+    tc.createSimpleTrace();
+    tc.createSampleRpcTrace();
+    HBaseTestUtil.stopReceiver(receiver);
+    Scan scan = new Scan();
+    scan.addFamily(Bytes.toBytes(HBaseSpanReceiver.DEFAULT_COLUMNFAMILY));
+    scan.setMaxVersions(1);
+    ArrayList<Span> spans = new ArrayList<Span>();
+    try {
+      ResultScanner scanner = htable.getScanner(scan);
+      Result result = null;
+      while ((result = scanner.next()) != null) {
+        for (Cell cell : result.listCells()) {
+          InputStream in = new ByteArrayInputStream(cell.getQualifierArray(),
+                                                    cell.getQualifierOffset(),
+                                                    cell.getQualifierLength());
+          spans.add(new TestSpan(SpanProtos.Span.parseFrom(in)));
+        }
+      }
+    } catch (IOException e) {
+      Assert.fail("failed to get spans from HBase. " + e.getMessage());
+    }
+
+    TraceTree traceTree = new TraceTree(spans);
+    Collection<Span> roots =
+        traceTree.getSpansByParent().find(Span.ROOT_SPAN_ID);
+    Assert.assertTrue("Trace tree must have roots", !roots.isEmpty());
+    Assert.assertEquals(3, roots.size());
+
+    Map<String, Span> descs = new HashMap<String, Span>();
+    for (Span root : roots) {
+      descs.put(root.getDescription(), root);
+    }
+    Assert.assertTrue(descs.keySet().contains(TraceCreator.RPC_TRACE_ROOT));
+    Assert.assertTrue(descs.keySet().contains(TraceCreator.SIMPLE_TRACE_ROOT));
+    Assert.assertTrue(descs.keySet().contains(TraceCreator.THREADED_TRACE_ROOT));
+
+    SpansByParent spansByParentId = traceTree.getSpansByParent();
+    Span rpcRoot = descs.get(TraceCreator.RPC_TRACE_ROOT);
+    Assert.assertEquals(1, spansByParentId.find(rpcRoot.getSpanId()).size());
+    Span rpcChild1 = spansByParentId.find(rpcRoot.getSpanId()).iterator().next();
+    Assert.assertEquals(1, spansByParentId.find(rpcChild1.getSpanId()).size());
+    Span rpcChild2 = spansByParentId.find(rpcChild1.getSpanId()).iterator().next();
+    Assert.assertEquals(1, spansByParentId.find(rpcChild2.getSpanId()).size());
+    Span rpcChild3 = spansByParentId.find(rpcChild2.getSpanId()).iterator().next();
+    Assert.assertEquals(0, spansByParentId.find(rpcChild3.getSpanId()).size());
+
+    Scan iscan = new Scan();
+    iscan.addColumn(Bytes.toBytes(HBaseSpanReceiver.DEFAULT_INDEXFAMILY),
+                    HBaseSpanReceiver.INDEX_SPAN_QUAL);
+    try {
+      ResultScanner scanner = htable.getScanner(iscan);
+      Result result = null;
+      while ((result = scanner.next()) != null) {
+        for (Cell cell : result.listCells()) {
+          InputStream in = new ByteArrayInputStream(cell.getValueArray(),
+                                                    cell.getValueOffset(),
+                                                    cell.getValueLength());
+          Assert.assertEquals(SpanProtos.Span.parseFrom(in).getParentId(),
+                              Span.ROOT_SPAN_ID);
+        }
+      }
+    } catch (IOException e) {
+      Assert.fail("failed to get spans from index family. " + e.getMessage());
+    }
+  }
+
+  private class TestSpan implements Span {
+    SpanProtos.Span span;
+
+    public TestSpan(SpanProtos.Span span) {
+      this.span = span;
+    }
+
+    @Override
+    public long getTraceId() {
+      return span.getTraceId();
+    }
+
+    @Override
+    public long getParentId() {
+      return span.getParentId();
+    }
+
+    @Override
+    public long getStartTimeMillis() {
+      return span.getStart();
+    }
+
+    @Override
+    public long getStopTimeMillis() {
+      return span.getStop();
+    }
+
+    @Override
+    public long getSpanId() {
+      return span.getSpanId();
+    }
+
+    @Override
+    public String getProcessId() {
+      return span.getProcessId();
+    }
+
+    @Override
+    public String getDescription() {
+      return span.getDescription();
+    }
+
+    @Override
+    public String toString() {
+      return String.format("Span{Id:0x%16x,parentId:0x%16x,pid:%s,desc:%s}",
+                           getSpanId(), getParentId(),
+                           getProcessId(), getDescription());
+    }
+
+    @Override
+    public Map<byte[], byte[]> getKVAnnotations() {
+      return Collections.emptyMap();
+    }
+
+    @Override
+    public List<TimelineAnnotation> getTimelineAnnotations() {
+      return Collections.emptyList();
+    }
+
+    @Override
+    public void addKVAnnotation(byte[] key, byte[] value) {}
+
+    @Override
+    public void addTimelineAnnotation(String msg) {}
+
+    @Override
+    public synchronized void stop() {}
+
+    @Override
+    public synchronized boolean isRunning() {
+      return false;
+    }
+
+    @Override
+    public synchronized long getAccumulatedMillis() {
+      return span.getStop() - span.getStart();
+    }
+
+    @Override
+    public Span child(String description) {
+      return null;
+    }
+
+    @Override
+    public String toJson() { return null; }
+  }
+}
diff --git a/htrace-hbase/src/test/java/org/apache/htrace/viewer/TestHBaseSpanViewer.java b/htrace-hbase/src/test/java/org/apache/htrace/viewer/TestHBaseSpanViewer.java
new file mode 100644
index 0000000..21e603a
--- /dev/null
+++ b/htrace-hbase/src/test/java/org/apache/htrace/viewer/TestHBaseSpanViewer.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.htrace.viewer;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.htrace.protobuf.generated.SpanProtos.Span;
+import org.apache.htrace.protobuf.generated.SpanProtos.TimelineAnnotation;
+import org.apache.htrace.viewer.HBaseSpanViewer;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class TestHBaseSpanViewer {
+  private static final Log LOG = LogFactory.getLog(TestHBaseSpanViewer.class);
+
+  @Test
+  public void testProtoToJson() {
+    Span.Builder sbuilder = Span.newBuilder();
+    TimelineAnnotation.Builder tlbuilder = TimelineAnnotation.newBuilder();
+    sbuilder.clear().setTraceId(1)
+                    .setParentId(2)
+                    .setStart(3)
+                    .setStop(4)
+                    .setSpanId(5)
+                    .setProcessId("pid")
+                    .setDescription("description");
+    for (int i = 0; i < 3; i++) {
+      sbuilder.addTimeline(tlbuilder.clear()
+                           .setTime(i)
+                           .setMessage("message" + i)
+                           .build());
+    }
+    Span span = sbuilder.build();
+    try {
+      String json = HBaseSpanViewer.toJsonString(span);
+      String expected =
+          "{\"trace_id\":\"1\","
+          + "\"parent_id\":\"2\","
+          + "\"start\":\"3\","
+          + "\"stop\":\"4\","
+          + "\"span_id\":\"5\","
+          + "\"process_id\":\"pid\","
+          + "\"description\":\"description\","
+          + "\"timeline\":["
+          + "{\"time\":\"0\",\"message\":\"message0\"},"
+          + "{\"time\":\"1\",\"message\":\"message1\"},"
+          + "{\"time\":\"2\",\"message\":\"message2\"}]}";
+      Assert.assertEquals(json, expected);
+    } catch (IOException e) {
+      Assert.fail("failed to get json from span. " + e.getMessage());
+    }
+  }
+
+  @Test
+  public void testProtoWithoutTimelineToJson() {
+    Span.Builder sbuilder = Span.newBuilder();
+    sbuilder.clear().setTraceId(1)
+                    .setParentId(2)
+                    .setStart(3)
+                    .setStop(4)
+                    .setSpanId(5)
+                    .setProcessId("pid")
+                    .setDescription("description");
+    Span span = sbuilder.build();
+    try {
+      String json = HBaseSpanViewer.toJsonString(span);
+      String expected =
+          "{\"trace_id\":\"1\","
+          + "\"parent_id\":\"2\","
+          + "\"start\":\"3\","
+          + "\"stop\":\"4\","
+          + "\"span_id\":\"5\","
+          + "\"process_id\":\"pid\","
+          + "\"description\":\"description\"}";
+      Assert.assertEquals(json, expected);
+    } catch (IOException e) {
+      Assert.fail("failed to get json from span. " + e.getMessage());
+    }
+  }
+}
diff --git a/htrace-hbase/timelines.png b/htrace-hbase/timelines.png
new file mode 100644
index 0000000..7542932
Binary files /dev/null and b/htrace-hbase/timelines.png differ
diff --git a/htrace-hbase/traces.png b/htrace-hbase/traces.png
new file mode 100644
index 0000000..ec54e44
Binary files /dev/null and b/htrace-hbase/traces.png differ
diff --git a/htrace-zipkin/pom.xml b/htrace-zipkin/pom.xml
index d9f8116..6462c3a 100644
--- a/htrace-zipkin/pom.xml
+++ b/htrace-zipkin/pom.xml
@@ -17,12 +17,13 @@ language governing permissions and limitations under the License. -->
 
   <parent>
     <artifactId>htrace</artifactId>
-    <groupId>org.htrace</groupId>
-    <version>3.0.4</version>
+    <groupId>org.apache.htrace</groupId>
+    <version>3.1.0-incubating</version>
+    <relativePath>..</relativePath>
   </parent>
 
   <name>htrace-zipkin</name>
-  <url>https://github.com/cloudera/htrace</url>
+  <url>http://incubator.apache.org/projects/htrace.html</url>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -31,6 +32,13 @@ language governing permissions and limitations under the License. -->
   <build>
     <plugins>
       <plugin>
+        <!--Make it so assembly:single does nothing in here-->
+        <artifactId>maven-assembly-plugin</artifactId>
+        <configuration>
+          <skipAssembly>true</skipAssembly>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
       </plugin>
@@ -66,7 +74,7 @@ language governing permissions and limitations under the License. -->
   <dependencies>
     <!-- Module deps. -->
     <dependency>
-      <groupId>org.htrace</groupId>
+      <groupId>org.apache.htrace</groupId>
       <artifactId>htrace-core</artifactId>
       <version>${project.version}</version>
       <scope>provided</scope>
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Annotation.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Annotation.java
deleted file mode 100644
index 70cb0b6..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Annotation.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class Annotation implements org.apache.thrift.TBase<Annotation, Annotation._Fields>, java.io.Serializable, Cloneable {
-  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Annotation");
-
-  private static final org.apache.thrift.protocol.TField TIMESTAMP_FIELD_DESC = new org.apache.thrift.protocol.TField("timestamp", org.apache.thrift.protocol.TType.I64, (short)1);
-  private static final org.apache.thrift.protocol.TField VALUE_FIELD_DESC = new org.apache.thrift.protocol.TField("value", org.apache.thrift.protocol.TType.STRING, (short)2);
-  private static final org.apache.thrift.protocol.TField HOST_FIELD_DESC = new org.apache.thrift.protocol.TField("host", org.apache.thrift.protocol.TType.STRUCT, (short)3);
-  private static final org.apache.thrift.protocol.TField DURATION_FIELD_DESC = new org.apache.thrift.protocol.TField("duration", org.apache.thrift.protocol.TType.I32, (short)4);
-
-  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-  static {
-    schemes.put(StandardScheme.class, new AnnotationStandardSchemeFactory());
-    schemes.put(TupleScheme.class, new AnnotationTupleSchemeFactory());
-  }
-
-  public long timestamp; // required
-  public String value; // required
-  public Endpoint host; // optional
-  public int duration; // optional
-
-  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-    TIMESTAMP((short)1, "timestamp"),
-    VALUE((short)2, "value"),
-    HOST((short)3, "host"),
-    DURATION((short)4, "duration");
-
-    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-    static {
-      for (_Fields field : EnumSet.allOf(_Fields.class)) {
-        byName.put(field.getFieldName(), field);
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, or null if its not found.
-     */
-    public static _Fields findByThriftId(int fieldId) {
-      switch(fieldId) {
-        case 1: // TIMESTAMP
-          return TIMESTAMP;
-        case 2: // VALUE
-          return VALUE;
-        case 3: // HOST
-          return HOST;
-        case 4: // DURATION
-          return DURATION;
-        default:
-          return null;
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, throwing an exception
-     * if it is not found.
-     */
-    public static _Fields findByThriftIdOrThrow(int fieldId) {
-      _Fields fields = findByThriftId(fieldId);
-      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-      return fields;
-    }
-
-    /**
-     * Find the _Fields constant that matches name, or null if its not found.
-     */
-    public static _Fields findByName(String name) {
-      return byName.get(name);
-    }
-
-    private final short _thriftId;
-    private final String _fieldName;
-
-    _Fields(short thriftId, String fieldName) {
-      _thriftId = thriftId;
-      _fieldName = fieldName;
-    }
-
-    public short getThriftFieldId() {
-      return _thriftId;
-    }
-
-    public String getFieldName() {
-      return _fieldName;
-    }
-  }
-
-  // isset id assignments
-  private static final int __TIMESTAMP_ISSET_ID = 0;
-  private static final int __DURATION_ISSET_ID = 1;
-  private byte __isset_bitfield = 0;
-  private _Fields optionals[] = {_Fields.HOST,_Fields.DURATION};
-  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-  static {
-    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-    tmpMap.put(_Fields.TIMESTAMP, new org.apache.thrift.meta_data.FieldMetaData("timestamp", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64)));
-    tmpMap.put(_Fields.VALUE, new org.apache.thrift.meta_data.FieldMetaData("value", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    tmpMap.put(_Fields.HOST, new org.apache.thrift.meta_data.FieldMetaData("host", org.apache.thrift.TFieldRequirementType.OPTIONAL, 
-        new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, Endpoint.class)));
-    tmpMap.put(_Fields.DURATION, new org.apache.thrift.meta_data.FieldMetaData("duration", org.apache.thrift.TFieldRequirementType.OPTIONAL, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32)));
-    metaDataMap = Collections.unmodifiableMap(tmpMap);
-    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Annotation.class, metaDataMap);
-  }
-
-  public Annotation() {
-  }
-
-  public Annotation(
-    long timestamp,
-    String value)
-  {
-    this();
-    this.timestamp = timestamp;
-    setTimestampIsSet(true);
-    this.value = value;
-  }
-
-  /**
-   * Performs a deep copy on <i>other</i>.
-   */
-  public Annotation(Annotation other) {
-    __isset_bitfield = other.__isset_bitfield;
-    this.timestamp = other.timestamp;
-    if (other.isSetValue()) {
-      this.value = other.value;
-    }
-    if (other.isSetHost()) {
-      this.host = new Endpoint(other.host);
-    }
-    this.duration = other.duration;
-  }
-
-  public Annotation deepCopy() {
-    return new Annotation(this);
-  }
-
-  @Override
-  public void clear() {
-    setTimestampIsSet(false);
-    this.timestamp = 0;
-    this.value = null;
-    this.host = null;
-    setDurationIsSet(false);
-    this.duration = 0;
-  }
-
-  public long getTimestamp() {
-    return this.timestamp;
-  }
-
-  public Annotation setTimestamp(long timestamp) {
-    this.timestamp = timestamp;
-    setTimestampIsSet(true);
-    return this;
-  }
-
-  public void unsetTimestamp() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __TIMESTAMP_ISSET_ID);
-  }
-
-  /** Returns true if field timestamp is set (has been assigned a value) and false otherwise */
-  public boolean isSetTimestamp() {
-    return EncodingUtils.testBit(__isset_bitfield, __TIMESTAMP_ISSET_ID);
-  }
-
-  public void setTimestampIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __TIMESTAMP_ISSET_ID, value);
-  }
-
-  public String getValue() {
-    return this.value;
-  }
-
-  public Annotation setValue(String value) {
-    this.value = value;
-    return this;
-  }
-
-  public void unsetValue() {
-    this.value = null;
-  }
-
-  /** Returns true if field value is set (has been assigned a value) and false otherwise */
-  public boolean isSetValue() {
-    return this.value != null;
-  }
-
-  public void setValueIsSet(boolean value) {
-    if (!value) {
-      this.value = null;
-    }
-  }
-
-  public Endpoint getHost() {
-    return this.host;
-  }
-
-  public Annotation setHost(Endpoint host) {
-    this.host = host;
-    return this;
-  }
-
-  public void unsetHost() {
-    this.host = null;
-  }
-
-  /** Returns true if field host is set (has been assigned a value) and false otherwise */
-  public boolean isSetHost() {
-    return this.host != null;
-  }
-
-  public void setHostIsSet(boolean value) {
-    if (!value) {
-      this.host = null;
-    }
-  }
-
-  public int getDuration() {
-    return this.duration;
-  }
-
-  public Annotation setDuration(int duration) {
-    this.duration = duration;
-    setDurationIsSet(true);
-    return this;
-  }
-
-  public void unsetDuration() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __DURATION_ISSET_ID);
-  }
-
-  /** Returns true if field duration is set (has been assigned a value) and false otherwise */
-  public boolean isSetDuration() {
-    return EncodingUtils.testBit(__isset_bitfield, __DURATION_ISSET_ID);
-  }
-
-  public void setDurationIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __DURATION_ISSET_ID, value);
-  }
-
-  public void setFieldValue(_Fields field, Object value) {
-    switch (field) {
-    case TIMESTAMP:
-      if (value == null) {
-        unsetTimestamp();
-      } else {
-        setTimestamp((Long)value);
-      }
-      break;
-
-    case VALUE:
-      if (value == null) {
-        unsetValue();
-      } else {
-        setValue((String)value);
-      }
-      break;
-
-    case HOST:
-      if (value == null) {
-        unsetHost();
-      } else {
-        setHost((Endpoint)value);
-      }
-      break;
-
-    case DURATION:
-      if (value == null) {
-        unsetDuration();
-      } else {
-        setDuration((Integer)value);
-      }
-      break;
-
-    }
-  }
-
-  public Object getFieldValue(_Fields field) {
-    switch (field) {
-    case TIMESTAMP:
-      return Long.valueOf(getTimestamp());
-
-    case VALUE:
-      return getValue();
-
-    case HOST:
-      return getHost();
-
-    case DURATION:
-      return Integer.valueOf(getDuration());
-
-    }
-    throw new IllegalStateException();
-  }
-
-  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-  public boolean isSet(_Fields field) {
-    if (field == null) {
-      throw new IllegalArgumentException();
-    }
-
-    switch (field) {
-    case TIMESTAMP:
-      return isSetTimestamp();
-    case VALUE:
-      return isSetValue();
-    case HOST:
-      return isSetHost();
-    case DURATION:
-      return isSetDuration();
-    }
-    throw new IllegalStateException();
-  }
-
-  @Override
-  public boolean equals(Object that) {
-    if (that == null)
-      return false;
-    if (that instanceof Annotation)
-      return this.equals((Annotation)that);
-    return false;
-  }
-
-  public boolean equals(Annotation that) {
-    if (that == null)
-      return false;
-
-    boolean this_present_timestamp = true;
-    boolean that_present_timestamp = true;
-    if (this_present_timestamp || that_present_timestamp) {
-      if (!(this_present_timestamp && that_present_timestamp))
-        return false;
-      if (this.timestamp != that.timestamp)
-        return false;
-    }
-
-    boolean this_present_value = true && this.isSetValue();
-    boolean that_present_value = true && that.isSetValue();
-    if (this_present_value || that_present_value) {
-      if (!(this_present_value && that_present_value))
-        return false;
-      if (!this.value.equals(that.value))
-        return false;
-    }
-
-    boolean this_present_host = true && this.isSetHost();
-    boolean that_present_host = true && that.isSetHost();
-    if (this_present_host || that_present_host) {
-      if (!(this_present_host && that_present_host))
-        return false;
-      if (!this.host.equals(that.host))
-        return false;
-    }
-
-    boolean this_present_duration = true && this.isSetDuration();
-    boolean that_present_duration = true && that.isSetDuration();
-    if (this_present_duration || that_present_duration) {
-      if (!(this_present_duration && that_present_duration))
-        return false;
-      if (this.duration != that.duration)
-        return false;
-    }
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return 0;
-  }
-
-  public int compareTo(Annotation other) {
-    if (!getClass().equals(other.getClass())) {
-      return getClass().getName().compareTo(other.getClass().getName());
-    }
-
-    int lastComparison = 0;
-    Annotation typedOther = (Annotation)other;
-
-    lastComparison = Boolean.valueOf(isSetTimestamp()).compareTo(typedOther.isSetTimestamp());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetTimestamp()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.timestamp, typedOther.timestamp);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetValue()).compareTo(typedOther.isSetValue());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetValue()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.value, typedOther.value);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetHost()).compareTo(typedOther.isSetHost());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetHost()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.host, typedOther.host);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetDuration()).compareTo(typedOther.isSetDuration());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetDuration()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.duration, typedOther.duration);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    return 0;
-  }
-
-  public _Fields fieldForId(int fieldId) {
-    return _Fields.findByThriftId(fieldId);
-  }
-
-  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-  }
-
-  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("Annotation(");
-    boolean first = true;
-
-    sb.append("timestamp:");
-    sb.append(this.timestamp);
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("value:");
-    if (this.value == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.value);
-    }
-    first = false;
-    if (isSetHost()) {
-      if (!first) sb.append(", ");
-      sb.append("host:");
-      if (this.host == null) {
-        sb.append("null");
-      } else {
-        sb.append(this.host);
-      }
-      first = false;
-    }
-    if (isSetDuration()) {
-      if (!first) sb.append(", ");
-      sb.append("duration:");
-      sb.append(this.duration);
-      first = false;
-    }
-    sb.append(")");
-    return sb.toString();
-  }
-
-  public void validate() throws org.apache.thrift.TException {
-    // check for required fields
-    // check for sub-struct validity
-    if (host != null) {
-      host.validate();
-    }
-  }
-
-  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-    try {
-      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-    try {
-      // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
-      __isset_bitfield = 0;
-      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private static class AnnotationStandardSchemeFactory implements SchemeFactory {
-    public AnnotationStandardScheme getScheme() {
-      return new AnnotationStandardScheme();
-    }
-  }
-
-  private static class AnnotationStandardScheme extends StandardScheme<Annotation> {
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot, Annotation struct) throws org.apache.thrift.TException {
-      org.apache.thrift.protocol.TField schemeField;
-      iprot.readStructBegin();
-      while (true)
-      {
-        schemeField = iprot.readFieldBegin();
-        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-          break;
-        }
-        switch (schemeField.id) {
-          case 1: // TIMESTAMP
-            if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
-              struct.timestamp = iprot.readI64();
-              struct.setTimestampIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 2: // VALUE
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.value = iprot.readString();
-              struct.setValueIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 3: // HOST
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) {
-              struct.host = new Endpoint();
-              struct.host.read(iprot);
-              struct.setHostIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 4: // DURATION
-            if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
-              struct.duration = iprot.readI32();
-              struct.setDurationIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          default:
-            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-        }
-        iprot.readFieldEnd();
-      }
-      iprot.readStructEnd();
-
-      // check for required fields of primitive type, which can't be checked in the validate method
-      struct.validate();
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot, Annotation struct) throws org.apache.thrift.TException {
-      struct.validate();
-
-      oprot.writeStructBegin(STRUCT_DESC);
-      oprot.writeFieldBegin(TIMESTAMP_FIELD_DESC);
-      oprot.writeI64(struct.timestamp);
-      oprot.writeFieldEnd();
-      if (struct.value != null) {
-        oprot.writeFieldBegin(VALUE_FIELD_DESC);
-        oprot.writeString(struct.value);
-        oprot.writeFieldEnd();
-      }
-      if (struct.host != null) {
-        if (struct.isSetHost()) {
-          oprot.writeFieldBegin(HOST_FIELD_DESC);
-          struct.host.write(oprot);
-          oprot.writeFieldEnd();
-        }
-      }
-      if (struct.isSetDuration()) {
-        oprot.writeFieldBegin(DURATION_FIELD_DESC);
-        oprot.writeI32(struct.duration);
-        oprot.writeFieldEnd();
-      }
-      oprot.writeFieldStop();
-      oprot.writeStructEnd();
-    }
-
-  }
-
-  private static class AnnotationTupleSchemeFactory implements SchemeFactory {
-    public AnnotationTupleScheme getScheme() {
-      return new AnnotationTupleScheme();
-    }
-  }
-
-  private static class AnnotationTupleScheme extends TupleScheme<Annotation> {
-
-    @Override
-    public void write(org.apache.thrift.protocol.TProtocol prot, Annotation struct) throws org.apache.thrift.TException {
-      TTupleProtocol oprot = (TTupleProtocol) prot;
-      BitSet optionals = new BitSet();
-      if (struct.isSetTimestamp()) {
-        optionals.set(0);
-      }
-      if (struct.isSetValue()) {
-        optionals.set(1);
-      }
-      if (struct.isSetHost()) {
-        optionals.set(2);
-      }
-      if (struct.isSetDuration()) {
-        optionals.set(3);
-      }
-      oprot.writeBitSet(optionals, 4);
-      if (struct.isSetTimestamp()) {
-        oprot.writeI64(struct.timestamp);
-      }
-      if (struct.isSetValue()) {
-        oprot.writeString(struct.value);
-      }
-      if (struct.isSetHost()) {
-        struct.host.write(oprot);
-      }
-      if (struct.isSetDuration()) {
-        oprot.writeI32(struct.duration);
-      }
-    }
-
-    @Override
-    public void read(org.apache.thrift.protocol.TProtocol prot, Annotation struct) throws org.apache.thrift.TException {
-      TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(4);
-      if (incoming.get(0)) {
-        struct.timestamp = iprot.readI64();
-        struct.setTimestampIsSet(true);
-      }
-      if (incoming.get(1)) {
-        struct.value = iprot.readString();
-        struct.setValueIsSet(true);
-      }
-      if (incoming.get(2)) {
-        struct.host = new Endpoint();
-        struct.host.read(iprot);
-        struct.setHostIsSet(true);
-      }
-      if (incoming.get(3)) {
-        struct.duration = iprot.readI32();
-        struct.setDurationIsSet(true);
-      }
-    }
-  }
-
-}
-
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/AnnotationType.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/AnnotationType.java
deleted file mode 100644
index b66df34..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/AnnotationType.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-
-import java.util.Map;
-import java.util.HashMap;
-import org.apache.thrift.TEnum;
-
-public enum AnnotationType implements org.apache.thrift.TEnum {
-  BOOL(0),
-  BYTES(1),
-  I16(2),
-  I32(3),
-  I64(4),
-  DOUBLE(5),
-  STRING(6);
-
-  private final int value;
-
-  private AnnotationType(int value) {
-    this.value = value;
-  }
-
-  /**
-   * Get the integer value of this enum value, as defined in the Thrift IDL.
-   */
-  public int getValue() {
-    return value;
-  }
-
-  /**
-   * Find a the enum type by its integer value, as defined in the Thrift IDL.
-   * @return null if the value is not found.
-   */
-  public static AnnotationType findByValue(int value) { 
-    switch (value) {
-      case 0:
-        return BOOL;
-      case 1:
-        return BYTES;
-      case 2:
-        return I16;
-      case 3:
-        return I32;
-      case 4:
-        return I64;
-      case 5:
-        return DOUBLE;
-      case 6:
-        return STRING;
-      default:
-        return null;
-    }
-  }
-}
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/BinaryAnnotation.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/BinaryAnnotation.java
deleted file mode 100644
index f762f62..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/BinaryAnnotation.java
+++ /dev/null
@@ -1,721 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class BinaryAnnotation implements org.apache.thrift.TBase<BinaryAnnotation, BinaryAnnotation._Fields>, java.io.Serializable, Cloneable {
-  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("BinaryAnnotation");
-
-  private static final org.apache.thrift.protocol.TField KEY_FIELD_DESC = new org.apache.thrift.protocol.TField("key", org.apache.thrift.protocol.TType.STRING, (short)1);
-  private static final org.apache.thrift.protocol.TField VALUE_FIELD_DESC = new org.apache.thrift.protocol.TField("value", org.apache.thrift.protocol.TType.STRING, (short)2);
-  private static final org.apache.thrift.protocol.TField ANNOTATION_TYPE_FIELD_DESC = new org.apache.thrift.protocol.TField("annotation_type", org.apache.thrift.protocol.TType.I32, (short)3);
-  private static final org.apache.thrift.protocol.TField HOST_FIELD_DESC = new org.apache.thrift.protocol.TField("host", org.apache.thrift.protocol.TType.STRUCT, (short)4);
-
-  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-  static {
-    schemes.put(StandardScheme.class, new BinaryAnnotationStandardSchemeFactory());
-    schemes.put(TupleScheme.class, new BinaryAnnotationTupleSchemeFactory());
-  }
-
-  public String key; // required
-  public ByteBuffer value; // required
-  /**
-   * 
-   * @see AnnotationType
-   */
-  public AnnotationType annotation_type; // required
-  public Endpoint host; // optional
-
-  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-    KEY((short)1, "key"),
-    VALUE((short)2, "value"),
-    /**
-     * 
-     * @see AnnotationType
-     */
-    ANNOTATION_TYPE((short)3, "annotation_type"),
-    HOST((short)4, "host");
-
-    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-    static {
-      for (_Fields field : EnumSet.allOf(_Fields.class)) {
-        byName.put(field.getFieldName(), field);
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, or null if its not found.
-     */
-    public static _Fields findByThriftId(int fieldId) {
-      switch(fieldId) {
-        case 1: // KEY
-          return KEY;
-        case 2: // VALUE
-          return VALUE;
-        case 3: // ANNOTATION_TYPE
-          return ANNOTATION_TYPE;
-        case 4: // HOST
-          return HOST;
-        default:
-          return null;
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, throwing an exception
-     * if it is not found.
-     */
-    public static _Fields findByThriftIdOrThrow(int fieldId) {
-      _Fields fields = findByThriftId(fieldId);
-      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-      return fields;
-    }
-
-    /**
-     * Find the _Fields constant that matches name, or null if its not found.
-     */
-    public static _Fields findByName(String name) {
-      return byName.get(name);
-    }
-
-    private final short _thriftId;
-    private final String _fieldName;
-
-    _Fields(short thriftId, String fieldName) {
-      _thriftId = thriftId;
-      _fieldName = fieldName;
-    }
-
-    public short getThriftFieldId() {
-      return _thriftId;
-    }
-
-    public String getFieldName() {
-      return _fieldName;
-    }
-  }
-
-  // isset id assignments
-  private _Fields optionals[] = {_Fields.HOST};
-  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-  static {
-    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-    tmpMap.put(_Fields.KEY, new org.apache.thrift.meta_data.FieldMetaData("key", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    tmpMap.put(_Fields.VALUE, new org.apache.thrift.meta_data.FieldMetaData("value", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING        , true)));
-    tmpMap.put(_Fields.ANNOTATION_TYPE, new org.apache.thrift.meta_data.FieldMetaData("annotation_type", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, AnnotationType.class)));
-    tmpMap.put(_Fields.HOST, new org.apache.thrift.meta_data.FieldMetaData("host", org.apache.thrift.TFieldRequirementType.OPTIONAL, 
-        new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, Endpoint.class)));
-    metaDataMap = Collections.unmodifiableMap(tmpMap);
-    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(BinaryAnnotation.class, metaDataMap);
-  }
-
-  public BinaryAnnotation() {
-  }
-
-  public BinaryAnnotation(
-    String key,
-    ByteBuffer value,
-    AnnotationType annotation_type)
-  {
-    this();
-    this.key = key;
-    this.value = value;
-    this.annotation_type = annotation_type;
-  }
-
-  /**
-   * Performs a deep copy on <i>other</i>.
-   */
-  public BinaryAnnotation(BinaryAnnotation other) {
-    if (other.isSetKey()) {
-      this.key = other.key;
-    }
-    if (other.isSetValue()) {
-      this.value = org.apache.thrift.TBaseHelper.copyBinary(other.value);
-;
-    }
-    if (other.isSetAnnotation_type()) {
-      this.annotation_type = other.annotation_type;
-    }
-    if (other.isSetHost()) {
-      this.host = new Endpoint(other.host);
-    }
-  }
-
-  public BinaryAnnotation deepCopy() {
-    return new BinaryAnnotation(this);
-  }
-
-  @Override
-  public void clear() {
-    this.key = null;
-    this.value = null;
-    this.annotation_type = null;
-    this.host = null;
-  }
-
-  public String getKey() {
-    return this.key;
-  }
-
-  public BinaryAnnotation setKey(String key) {
-    this.key = key;
-    return this;
-  }
-
-  public void unsetKey() {
-    this.key = null;
-  }
-
-  /** Returns true if field key is set (has been assigned a value) and false otherwise */
-  public boolean isSetKey() {
-    return this.key != null;
-  }
-
-  public void setKeyIsSet(boolean value) {
-    if (!value) {
-      this.key = null;
-    }
-  }
-
-  public byte[] getValue() {
-    setValue(org.apache.thrift.TBaseHelper.rightSize(value));
-    return value == null ? null : value.array();
-  }
-
-  public ByteBuffer bufferForValue() {
-    return value;
-  }
-
-  public BinaryAnnotation setValue(byte[] value) {
-    setValue(value == null ? (ByteBuffer)null : ByteBuffer.wrap(value));
-    return this;
-  }
-
-  public BinaryAnnotation setValue(ByteBuffer value) {
-    this.value = value;
-    return this;
-  }
-
-  public void unsetValue() {
-    this.value = null;
-  }
-
-  /** Returns true if field value is set (has been assigned a value) and false otherwise */
-  public boolean isSetValue() {
-    return this.value != null;
-  }
-
-  public void setValueIsSet(boolean value) {
-    if (!value) {
-      this.value = null;
-    }
-  }
-
-  /**
-   * 
-   * @see AnnotationType
-   */
-  public AnnotationType getAnnotation_type() {
-    return this.annotation_type;
-  }
-
-  /**
-   * 
-   * @see AnnotationType
-   */
-  public BinaryAnnotation setAnnotation_type(AnnotationType annotation_type) {
-    this.annotation_type = annotation_type;
-    return this;
-  }
-
-  public void unsetAnnotation_type() {
-    this.annotation_type = null;
-  }
-
-  /** Returns true if field annotation_type is set (has been assigned a value) and false otherwise */
-  public boolean isSetAnnotation_type() {
-    return this.annotation_type != null;
-  }
-
-  public void setAnnotation_typeIsSet(boolean value) {
-    if (!value) {
-      this.annotation_type = null;
-    }
-  }
-
-  public Endpoint getHost() {
-    return this.host;
-  }
-
-  public BinaryAnnotation setHost(Endpoint host) {
-    this.host = host;
-    return this;
-  }
-
-  public void unsetHost() {
-    this.host = null;
-  }
-
-  /** Returns true if field host is set (has been assigned a value) and false otherwise */
-  public boolean isSetHost() {
-    return this.host != null;
-  }
-
-  public void setHostIsSet(boolean value) {
-    if (!value) {
-      this.host = null;
-    }
-  }
-
-  public void setFieldValue(_Fields field, Object value) {
-    switch (field) {
-    case KEY:
-      if (value == null) {
-        unsetKey();
-      } else {
-        setKey((String)value);
-      }
-      break;
-
-    case VALUE:
-      if (value == null) {
-        unsetValue();
-      } else {
-        setValue((ByteBuffer)value);
-      }
-      break;
-
-    case ANNOTATION_TYPE:
-      if (value == null) {
-        unsetAnnotation_type();
-      } else {
-        setAnnotation_type((AnnotationType)value);
-      }
-      break;
-
-    case HOST:
-      if (value == null) {
-        unsetHost();
-      } else {
-        setHost((Endpoint)value);
-      }
-      break;
-
-    }
-  }
-
-  public Object getFieldValue(_Fields field) {
-    switch (field) {
-    case KEY:
-      return getKey();
-
-    case VALUE:
-      return getValue();
-
-    case ANNOTATION_TYPE:
-      return getAnnotation_type();
-
-    case HOST:
-      return getHost();
-
-    }
-    throw new IllegalStateException();
-  }
-
-  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-  public boolean isSet(_Fields field) {
-    if (field == null) {
-      throw new IllegalArgumentException();
-    }
-
-    switch (field) {
-    case KEY:
-      return isSetKey();
-    case VALUE:
-      return isSetValue();
-    case ANNOTATION_TYPE:
-      return isSetAnnotation_type();
-    case HOST:
-      return isSetHost();
-    }
-    throw new IllegalStateException();
-  }
-
-  @Override
-  public boolean equals(Object that) {
-    if (that == null)
-      return false;
-    if (that instanceof BinaryAnnotation)
-      return this.equals((BinaryAnnotation)that);
-    return false;
-  }
-
-  public boolean equals(BinaryAnnotation that) {
-    if (that == null)
-      return false;
-
-    boolean this_present_key = true && this.isSetKey();
-    boolean that_present_key = true && that.isSetKey();
-    if (this_present_key || that_present_key) {
-      if (!(this_present_key && that_present_key))
-        return false;
-      if (!this.key.equals(that.key))
-        return false;
-    }
-
-    boolean this_present_value = true && this.isSetValue();
-    boolean that_present_value = true && that.isSetValue();
-    if (this_present_value || that_present_value) {
-      if (!(this_present_value && that_present_value))
-        return false;
-      if (!this.value.equals(that.value))
-        return false;
-    }
-
-    boolean this_present_annotation_type = true && this.isSetAnnotation_type();
-    boolean that_present_annotation_type = true && that.isSetAnnotation_type();
-    if (this_present_annotation_type || that_present_annotation_type) {
-      if (!(this_present_annotation_type && that_present_annotation_type))
-        return false;
-      if (!this.annotation_type.equals(that.annotation_type))
-        return false;
-    }
-
-    boolean this_present_host = true && this.isSetHost();
-    boolean that_present_host = true && that.isSetHost();
-    if (this_present_host || that_present_host) {
-      if (!(this_present_host && that_present_host))
-        return false;
-      if (!this.host.equals(that.host))
-        return false;
-    }
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return 0;
-  }
-
-  public int compareTo(BinaryAnnotation other) {
-    if (!getClass().equals(other.getClass())) {
-      return getClass().getName().compareTo(other.getClass().getName());
-    }
-
-    int lastComparison = 0;
-    BinaryAnnotation typedOther = (BinaryAnnotation)other;
-
-    lastComparison = Boolean.valueOf(isSetKey()).compareTo(typedOther.isSetKey());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetKey()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.key, typedOther.key);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetValue()).compareTo(typedOther.isSetValue());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetValue()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.value, typedOther.value);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetAnnotation_type()).compareTo(typedOther.isSetAnnotation_type());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetAnnotation_type()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.annotation_type, typedOther.annotation_type);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetHost()).compareTo(typedOther.isSetHost());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetHost()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.host, typedOther.host);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    return 0;
-  }
-
-  public _Fields fieldForId(int fieldId) {
-    return _Fields.findByThriftId(fieldId);
-  }
-
-  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-  }
-
-  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("BinaryAnnotation(");
-    boolean first = true;
-
-    sb.append("key:");
-    if (this.key == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.key);
-    }
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("value:");
-    if (this.value == null) {
-      sb.append("null");
-    } else {
-      org.apache.thrift.TBaseHelper.toString(this.value, sb);
-    }
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("annotation_type:");
-    if (this.annotation_type == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.annotation_type);
-    }
-    first = false;
-    if (isSetHost()) {
-      if (!first) sb.append(", ");
-      sb.append("host:");
-      if (this.host == null) {
-        sb.append("null");
-      } else {
-        sb.append(this.host);
-      }
-      first = false;
-    }
-    sb.append(")");
-    return sb.toString();
-  }
-
-  public void validate() throws org.apache.thrift.TException {
-    // check for required fields
-    // check for sub-struct validity
-    if (host != null) {
-      host.validate();
-    }
-  }
-
-  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-    try {
-      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-    try {
-      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private static class BinaryAnnotationStandardSchemeFactory implements SchemeFactory {
-    public BinaryAnnotationStandardScheme getScheme() {
-      return new BinaryAnnotationStandardScheme();
-    }
-  }
-
-  private static class BinaryAnnotationStandardScheme extends StandardScheme<BinaryAnnotation> {
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot, BinaryAnnotation struct) throws org.apache.thrift.TException {
-      org.apache.thrift.protocol.TField schemeField;
-      iprot.readStructBegin();
-      while (true)
-      {
-        schemeField = iprot.readFieldBegin();
-        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-          break;
-        }
-        switch (schemeField.id) {
-          case 1: // KEY
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.key = iprot.readString();
-              struct.setKeyIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 2: // VALUE
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.value = iprot.readBinary();
-              struct.setValueIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 3: // ANNOTATION_TYPE
-            if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
-              struct.annotation_type = AnnotationType.findByValue(iprot.readI32());
-              struct.setAnnotation_typeIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 4: // HOST
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) {
-              struct.host = new Endpoint();
-              struct.host.read(iprot);
-              struct.setHostIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          default:
-            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-        }
-        iprot.readFieldEnd();
-      }
-      iprot.readStructEnd();
-
-      // check for required fields of primitive type, which can't be checked in the validate method
-      struct.validate();
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot, BinaryAnnotation struct) throws org.apache.thrift.TException {
-      struct.validate();
-
-      oprot.writeStructBegin(STRUCT_DESC);
-      if (struct.key != null) {
-        oprot.writeFieldBegin(KEY_FIELD_DESC);
-        oprot.writeString(struct.key);
-        oprot.writeFieldEnd();
-      }
-      if (struct.value != null) {
-        oprot.writeFieldBegin(VALUE_FIELD_DESC);
-        oprot.writeBinary(struct.value);
-        oprot.writeFieldEnd();
-      }
-      if (struct.annotation_type != null) {
-        oprot.writeFieldBegin(ANNOTATION_TYPE_FIELD_DESC);
-        oprot.writeI32(struct.annotation_type.getValue());
-        oprot.writeFieldEnd();
-      }
-      if (struct.host != null) {
-        if (struct.isSetHost()) {
-          oprot.writeFieldBegin(HOST_FIELD_DESC);
-          struct.host.write(oprot);
-          oprot.writeFieldEnd();
-        }
-      }
-      oprot.writeFieldStop();
-      oprot.writeStructEnd();
-    }
-
-  }
-
-  private static class BinaryAnnotationTupleSchemeFactory implements SchemeFactory {
-    public BinaryAnnotationTupleScheme getScheme() {
-      return new BinaryAnnotationTupleScheme();
-    }
-  }
-
-  private static class BinaryAnnotationTupleScheme extends TupleScheme<BinaryAnnotation> {
-
-    @Override
-    public void write(org.apache.thrift.protocol.TProtocol prot, BinaryAnnotation struct) throws org.apache.thrift.TException {
-      TTupleProtocol oprot = (TTupleProtocol) prot;
-      BitSet optionals = new BitSet();
-      if (struct.isSetKey()) {
-        optionals.set(0);
-      }
-      if (struct.isSetValue()) {
-        optionals.set(1);
-      }
-      if (struct.isSetAnnotation_type()) {
-        optionals.set(2);
-      }
-      if (struct.isSetHost()) {
-        optionals.set(3);
-      }
-      oprot.writeBitSet(optionals, 4);
-      if (struct.isSetKey()) {
-        oprot.writeString(struct.key);
-      }
-      if (struct.isSetValue()) {
-        oprot.writeBinary(struct.value);
-      }
-      if (struct.isSetAnnotation_type()) {
-        oprot.writeI32(struct.annotation_type.getValue());
-      }
-      if (struct.isSetHost()) {
-        struct.host.write(oprot);
-      }
-    }
-
-    @Override
-    public void read(org.apache.thrift.protocol.TProtocol prot, BinaryAnnotation struct) throws org.apache.thrift.TException {
-      TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(4);
-      if (incoming.get(0)) {
-        struct.key = iprot.readString();
-        struct.setKeyIsSet(true);
-      }
-      if (incoming.get(1)) {
-        struct.value = iprot.readBinary();
-        struct.setValueIsSet(true);
-      }
-      if (incoming.get(2)) {
-        struct.annotation_type = AnnotationType.findByValue(iprot.readI32());
-        struct.setAnnotation_typeIsSet(true);
-      }
-      if (incoming.get(3)) {
-        struct.host = new Endpoint();
-        struct.host.read(iprot);
-        struct.setHostIsSet(true);
-      }
-    }
-  }
-
-}
-
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Endpoint.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Endpoint.java
deleted file mode 100644
index 9877509..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Endpoint.java
+++ /dev/null
@@ -1,578 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class Endpoint implements org.apache.thrift.TBase<Endpoint, Endpoint._Fields>, java.io.Serializable, Cloneable {
-  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Endpoint");
-
-  private static final org.apache.thrift.protocol.TField IPV4_FIELD_DESC = new org.apache.thrift.protocol.TField("ipv4", org.apache.thrift.protocol.TType.I32, (short)1);
-  private static final org.apache.thrift.protocol.TField PORT_FIELD_DESC = new org.apache.thrift.protocol.TField("port", org.apache.thrift.protocol.TType.I16, (short)2);
-  private static final org.apache.thrift.protocol.TField SERVICE_NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("service_name", org.apache.thrift.protocol.TType.STRING, (short)3);
-
-  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-  static {
-    schemes.put(StandardScheme.class, new EndpointStandardSchemeFactory());
-    schemes.put(TupleScheme.class, new EndpointTupleSchemeFactory());
-  }
-
-  public int ipv4; // required
-  public short port; // required
-  public String service_name; // required
-
-  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-    IPV4((short)1, "ipv4"),
-    PORT((short)2, "port"),
-    SERVICE_NAME((short)3, "service_name");
-
-    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-    static {
-      for (_Fields field : EnumSet.allOf(_Fields.class)) {
-        byName.put(field.getFieldName(), field);
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, or null if its not found.
-     */
-    public static _Fields findByThriftId(int fieldId) {
-      switch(fieldId) {
-        case 1: // IPV4
-          return IPV4;
-        case 2: // PORT
-          return PORT;
-        case 3: // SERVICE_NAME
-          return SERVICE_NAME;
-        default:
-          return null;
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, throwing an exception
-     * if it is not found.
-     */
-    public static _Fields findByThriftIdOrThrow(int fieldId) {
-      _Fields fields = findByThriftId(fieldId);
-      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-      return fields;
-    }
-
-    /**
-     * Find the _Fields constant that matches name, or null if its not found.
-     */
-    public static _Fields findByName(String name) {
-      return byName.get(name);
-    }
-
-    private final short _thriftId;
-    private final String _fieldName;
-
-    _Fields(short thriftId, String fieldName) {
-      _thriftId = thriftId;
-      _fieldName = fieldName;
-    }
-
-    public short getThriftFieldId() {
-      return _thriftId;
-    }
-
-    public String getFieldName() {
-      return _fieldName;
-    }
-  }
-
-  // isset id assignments
-  private static final int __IPV4_ISSET_ID = 0;
-  private static final int __PORT_ISSET_ID = 1;
-  private byte __isset_bitfield = 0;
-  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-  static {
-    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-    tmpMap.put(_Fields.IPV4, new org.apache.thrift.meta_data.FieldMetaData("ipv4", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32)));
-    tmpMap.put(_Fields.PORT, new org.apache.thrift.meta_data.FieldMetaData("port", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I16)));
-    tmpMap.put(_Fields.SERVICE_NAME, new org.apache.thrift.meta_data.FieldMetaData("service_name", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    metaDataMap = Collections.unmodifiableMap(tmpMap);
-    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Endpoint.class, metaDataMap);
-  }
-
-  public Endpoint() {
-  }
-
-  public Endpoint(
-    int ipv4,
-    short port,
-    String service_name)
-  {
-    this();
-    this.ipv4 = ipv4;
-    setIpv4IsSet(true);
-    this.port = port;
-    setPortIsSet(true);
-    this.service_name = service_name;
-  }
-
-  /**
-   * Performs a deep copy on <i>other</i>.
-   */
-  public Endpoint(Endpoint other) {
-    __isset_bitfield = other.__isset_bitfield;
-    this.ipv4 = other.ipv4;
-    this.port = other.port;
-    if (other.isSetService_name()) {
-      this.service_name = other.service_name;
-    }
-  }
-
-  public Endpoint deepCopy() {
-    return new Endpoint(this);
-  }
-
-  @Override
-  public void clear() {
-    setIpv4IsSet(false);
-    this.ipv4 = 0;
-    setPortIsSet(false);
-    this.port = 0;
-    this.service_name = null;
-  }
-
-  public int getIpv4() {
-    return this.ipv4;
-  }
-
-  public Endpoint setIpv4(int ipv4) {
-    this.ipv4 = ipv4;
-    setIpv4IsSet(true);
-    return this;
-  }
-
-  public void unsetIpv4() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __IPV4_ISSET_ID);
-  }
-
-  /** Returns true if field ipv4 is set (has been assigned a value) and false otherwise */
-  public boolean isSetIpv4() {
-    return EncodingUtils.testBit(__isset_bitfield, __IPV4_ISSET_ID);
-  }
-
-  public void setIpv4IsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __IPV4_ISSET_ID, value);
-  }
-
-  public short getPort() {
-    return this.port;
-  }
-
-  public Endpoint setPort(short port) {
-    this.port = port;
-    setPortIsSet(true);
-    return this;
-  }
-
-  public void unsetPort() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __PORT_ISSET_ID);
-  }
-
-  /** Returns true if field port is set (has been assigned a value) and false otherwise */
-  public boolean isSetPort() {
-    return EncodingUtils.testBit(__isset_bitfield, __PORT_ISSET_ID);
-  }
-
-  public void setPortIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __PORT_ISSET_ID, value);
-  }
-
-  public String getService_name() {
-    return this.service_name;
-  }
-
-  public Endpoint setService_name(String service_name) {
-    this.service_name = service_name;
-    return this;
-  }
-
-  public void unsetService_name() {
-    this.service_name = null;
-  }
-
-  /** Returns true if field service_name is set (has been assigned a value) and false otherwise */
-  public boolean isSetService_name() {
-    return this.service_name != null;
-  }
-
-  public void setService_nameIsSet(boolean value) {
-    if (!value) {
-      this.service_name = null;
-    }
-  }
-
-  public void setFieldValue(_Fields field, Object value) {
-    switch (field) {
-    case IPV4:
-      if (value == null) {
-        unsetIpv4();
-      } else {
-        setIpv4((Integer)value);
-      }
-      break;
-
-    case PORT:
-      if (value == null) {
-        unsetPort();
-      } else {
-        setPort((Short)value);
-      }
-      break;
-
-    case SERVICE_NAME:
-      if (value == null) {
-        unsetService_name();
-      } else {
-        setService_name((String)value);
-      }
-      break;
-
-    }
-  }
-
-  public Object getFieldValue(_Fields field) {
-    switch (field) {
-    case IPV4:
-      return Integer.valueOf(getIpv4());
-
-    case PORT:
-      return Short.valueOf(getPort());
-
-    case SERVICE_NAME:
-      return getService_name();
-
-    }
-    throw new IllegalStateException();
-  }
-
-  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-  public boolean isSet(_Fields field) {
-    if (field == null) {
-      throw new IllegalArgumentException();
-    }
-
-    switch (field) {
-    case IPV4:
-      return isSetIpv4();
-    case PORT:
-      return isSetPort();
-    case SERVICE_NAME:
-      return isSetService_name();
-    }
-    throw new IllegalStateException();
-  }
-
-  @Override
-  public boolean equals(Object that) {
-    if (that == null)
-      return false;
-    if (that instanceof Endpoint)
-      return this.equals((Endpoint)that);
-    return false;
-  }
-
-  public boolean equals(Endpoint that) {
-    if (that == null)
-      return false;
-
-    boolean this_present_ipv4 = true;
-    boolean that_present_ipv4 = true;
-    if (this_present_ipv4 || that_present_ipv4) {
-      if (!(this_present_ipv4 && that_present_ipv4))
-        return false;
-      if (this.ipv4 != that.ipv4)
-        return false;
-    }
-
-    boolean this_present_port = true;
-    boolean that_present_port = true;
-    if (this_present_port || that_present_port) {
-      if (!(this_present_port && that_present_port))
-        return false;
-      if (this.port != that.port)
-        return false;
-    }
-
-    boolean this_present_service_name = true && this.isSetService_name();
-    boolean that_present_service_name = true && that.isSetService_name();
-    if (this_present_service_name || that_present_service_name) {
-      if (!(this_present_service_name && that_present_service_name))
-        return false;
-      if (!this.service_name.equals(that.service_name))
-        return false;
-    }
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return 0;
-  }
-
-  public int compareTo(Endpoint other) {
-    if (!getClass().equals(other.getClass())) {
-      return getClass().getName().compareTo(other.getClass().getName());
-    }
-
-    int lastComparison = 0;
-    Endpoint typedOther = (Endpoint)other;
-
-    lastComparison = Boolean.valueOf(isSetIpv4()).compareTo(typedOther.isSetIpv4());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetIpv4()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.ipv4, typedOther.ipv4);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetPort()).compareTo(typedOther.isSetPort());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetPort()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.port, typedOther.port);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetService_name()).compareTo(typedOther.isSetService_name());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetService_name()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.service_name, typedOther.service_name);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    return 0;
-  }
-
-  public _Fields fieldForId(int fieldId) {
-    return _Fields.findByThriftId(fieldId);
-  }
-
-  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-  }
-
-  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("Endpoint(");
-    boolean first = true;
-
-    sb.append("ipv4:");
-    sb.append(this.ipv4);
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("port:");
-    sb.append(this.port);
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("service_name:");
-    if (this.service_name == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.service_name);
-    }
-    first = false;
-    sb.append(")");
-    return sb.toString();
-  }
-
-  public void validate() throws org.apache.thrift.TException {
-    // check for required fields
-    // check for sub-struct validity
-  }
-
-  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-    try {
-      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-    try {
-      // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
-      __isset_bitfield = 0;
-      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private static class EndpointStandardSchemeFactory implements SchemeFactory {
-    public EndpointStandardScheme getScheme() {
-      return new EndpointStandardScheme();
-    }
-  }
-
-  private static class EndpointStandardScheme extends StandardScheme<Endpoint> {
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot, Endpoint struct) throws org.apache.thrift.TException {
-      org.apache.thrift.protocol.TField schemeField;
-      iprot.readStructBegin();
-      while (true)
-      {
-        schemeField = iprot.readFieldBegin();
-        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-          break;
-        }
-        switch (schemeField.id) {
-          case 1: // IPV4
-            if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
-              struct.ipv4 = iprot.readI32();
-              struct.setIpv4IsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 2: // PORT
-            if (schemeField.type == org.apache.thrift.protocol.TType.I16) {
-              struct.port = iprot.readI16();
-              struct.setPortIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 3: // SERVICE_NAME
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.service_name = iprot.readString();
-              struct.setService_nameIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          default:
-            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-        }
-        iprot.readFieldEnd();
-      }
-      iprot.readStructEnd();
-
-      // check for required fields of primitive type, which can't be checked in the validate method
-      struct.validate();
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot, Endpoint struct) throws org.apache.thrift.TException {
-      struct.validate();
-
-      oprot.writeStructBegin(STRUCT_DESC);
-      oprot.writeFieldBegin(IPV4_FIELD_DESC);
-      oprot.writeI32(struct.ipv4);
-      oprot.writeFieldEnd();
-      oprot.writeFieldBegin(PORT_FIELD_DESC);
-      oprot.writeI16(struct.port);
-      oprot.writeFieldEnd();
-      if (struct.service_name != null) {
-        oprot.writeFieldBegin(SERVICE_NAME_FIELD_DESC);
-        oprot.writeString(struct.service_name);
-        oprot.writeFieldEnd();
-      }
-      oprot.writeFieldStop();
-      oprot.writeStructEnd();
-    }
-
-  }
-
-  private static class EndpointTupleSchemeFactory implements SchemeFactory {
-    public EndpointTupleScheme getScheme() {
-      return new EndpointTupleScheme();
-    }
-  }
-
-  private static class EndpointTupleScheme extends TupleScheme<Endpoint> {
-
-    @Override
-    public void write(org.apache.thrift.protocol.TProtocol prot, Endpoint struct) throws org.apache.thrift.TException {
-      TTupleProtocol oprot = (TTupleProtocol) prot;
-      BitSet optionals = new BitSet();
-      if (struct.isSetIpv4()) {
-        optionals.set(0);
-      }
-      if (struct.isSetPort()) {
-        optionals.set(1);
-      }
-      if (struct.isSetService_name()) {
-        optionals.set(2);
-      }
-      oprot.writeBitSet(optionals, 3);
-      if (struct.isSetIpv4()) {
-        oprot.writeI32(struct.ipv4);
-      }
-      if (struct.isSetPort()) {
-        oprot.writeI16(struct.port);
-      }
-      if (struct.isSetService_name()) {
-        oprot.writeString(struct.service_name);
-      }
-    }
-
-    @Override
-    public void read(org.apache.thrift.protocol.TProtocol prot, Endpoint struct) throws org.apache.thrift.TException {
-      TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(3);
-      if (incoming.get(0)) {
-        struct.ipv4 = iprot.readI32();
-        struct.setIpv4IsSet(true);
-      }
-      if (incoming.get(1)) {
-        struct.port = iprot.readI16();
-        struct.setPortIsSet(true);
-      }
-      if (incoming.get(2)) {
-        struct.service_name = iprot.readString();
-        struct.setService_nameIsSet(true);
-      }
-    }
-  }
-
-}
-
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/LogEntry.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/LogEntry.java
deleted file mode 100644
index 2ea3c10..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/LogEntry.java
+++ /dev/null
@@ -1,486 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class LogEntry implements org.apache.thrift.TBase<LogEntry, LogEntry._Fields>, java.io.Serializable, Cloneable {
-  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("LogEntry");
-
-  private static final org.apache.thrift.protocol.TField CATEGORY_FIELD_DESC = new org.apache.thrift.protocol.TField("category", org.apache.thrift.protocol.TType.STRING, (short)1);
-  private static final org.apache.thrift.protocol.TField MESSAGE_FIELD_DESC = new org.apache.thrift.protocol.TField("message", org.apache.thrift.protocol.TType.STRING, (short)2);
-
-  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-  static {
-    schemes.put(StandardScheme.class, new LogEntryStandardSchemeFactory());
-    schemes.put(TupleScheme.class, new LogEntryTupleSchemeFactory());
-  }
-
-  public String category; // required
-  public String message; // required
-
-  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-    CATEGORY((short)1, "category"),
-    MESSAGE((short)2, "message");
-
-    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-    static {
-      for (_Fields field : EnumSet.allOf(_Fields.class)) {
-        byName.put(field.getFieldName(), field);
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, or null if its not found.
-     */
-    public static _Fields findByThriftId(int fieldId) {
-      switch(fieldId) {
-        case 1: // CATEGORY
-          return CATEGORY;
-        case 2: // MESSAGE
-          return MESSAGE;
-        default:
-          return null;
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, throwing an exception
-     * if it is not found.
-     */
-    public static _Fields findByThriftIdOrThrow(int fieldId) {
-      _Fields fields = findByThriftId(fieldId);
-      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-      return fields;
-    }
-
-    /**
-     * Find the _Fields constant that matches name, or null if its not found.
-     */
-    public static _Fields findByName(String name) {
-      return byName.get(name);
-    }
-
-    private final short _thriftId;
-    private final String _fieldName;
-
-    _Fields(short thriftId, String fieldName) {
-      _thriftId = thriftId;
-      _fieldName = fieldName;
-    }
-
-    public short getThriftFieldId() {
-      return _thriftId;
-    }
-
-    public String getFieldName() {
-      return _fieldName;
-    }
-  }
-
-  // isset id assignments
-  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-  static {
-    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-    tmpMap.put(_Fields.CATEGORY, new org.apache.thrift.meta_data.FieldMetaData("category", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    tmpMap.put(_Fields.MESSAGE, new org.apache.thrift.meta_data.FieldMetaData("message", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    metaDataMap = Collections.unmodifiableMap(tmpMap);
-    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(LogEntry.class, metaDataMap);
-  }
-
-  public LogEntry() {
-  }
-
-  public LogEntry(
-    String category,
-    String message)
-  {
-    this();
-    this.category = category;
-    this.message = message;
-  }
-
-  /**
-   * Performs a deep copy on <i>other</i>.
-   */
-  public LogEntry(LogEntry other) {
-    if (other.isSetCategory()) {
-      this.category = other.category;
-    }
-    if (other.isSetMessage()) {
-      this.message = other.message;
-    }
-  }
-
-  public LogEntry deepCopy() {
-    return new LogEntry(this);
-  }
-
-  @Override
-  public void clear() {
-    this.category = null;
-    this.message = null;
-  }
-
-  public String getCategory() {
-    return this.category;
-  }
-
-  public LogEntry setCategory(String category) {
-    this.category = category;
-    return this;
-  }
-
-  public void unsetCategory() {
-    this.category = null;
-  }
-
-  /** Returns true if field category is set (has been assigned a value) and false otherwise */
-  public boolean isSetCategory() {
-    return this.category != null;
-  }
-
-  public void setCategoryIsSet(boolean value) {
-    if (!value) {
-      this.category = null;
-    }
-  }
-
-  public String getMessage() {
-    return this.message;
-  }
-
-  public LogEntry setMessage(String message) {
-    this.message = message;
-    return this;
-  }
-
-  public void unsetMessage() {
-    this.message = null;
-  }
-
-  /** Returns true if field message is set (has been assigned a value) and false otherwise */
-  public boolean isSetMessage() {
-    return this.message != null;
-  }
-
-  public void setMessageIsSet(boolean value) {
-    if (!value) {
-      this.message = null;
-    }
-  }
-
-  public void setFieldValue(_Fields field, Object value) {
-    switch (field) {
-    case CATEGORY:
-      if (value == null) {
-        unsetCategory();
-      } else {
-        setCategory((String)value);
-      }
-      break;
-
-    case MESSAGE:
-      if (value == null) {
-        unsetMessage();
-      } else {
-        setMessage((String)value);
-      }
-      break;
-
-    }
-  }
-
-  public Object getFieldValue(_Fields field) {
-    switch (field) {
-    case CATEGORY:
-      return getCategory();
-
-    case MESSAGE:
-      return getMessage();
-
-    }
-    throw new IllegalStateException();
-  }
-
-  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-  public boolean isSet(_Fields field) {
-    if (field == null) {
-      throw new IllegalArgumentException();
-    }
-
-    switch (field) {
-    case CATEGORY:
-      return isSetCategory();
-    case MESSAGE:
-      return isSetMessage();
-    }
-    throw new IllegalStateException();
-  }
-
-  @Override
-  public boolean equals(Object that) {
-    if (that == null)
-      return false;
-    if (that instanceof LogEntry)
-      return this.equals((LogEntry)that);
-    return false;
-  }
-
-  public boolean equals(LogEntry that) {
-    if (that == null)
-      return false;
-
-    boolean this_present_category = true && this.isSetCategory();
-    boolean that_present_category = true && that.isSetCategory();
-    if (this_present_category || that_present_category) {
-      if (!(this_present_category && that_present_category))
-        return false;
-      if (!this.category.equals(that.category))
-        return false;
-    }
-
-    boolean this_present_message = true && this.isSetMessage();
-    boolean that_present_message = true && that.isSetMessage();
-    if (this_present_message || that_present_message) {
-      if (!(this_present_message && that_present_message))
-        return false;
-      if (!this.message.equals(that.message))
-        return false;
-    }
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return 0;
-  }
-
-  public int compareTo(LogEntry other) {
-    if (!getClass().equals(other.getClass())) {
-      return getClass().getName().compareTo(other.getClass().getName());
-    }
-
-    int lastComparison = 0;
-    LogEntry typedOther = (LogEntry)other;
-
-    lastComparison = Boolean.valueOf(isSetCategory()).compareTo(typedOther.isSetCategory());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetCategory()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.category, typedOther.category);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetMessage()).compareTo(typedOther.isSetMessage());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetMessage()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.message, typedOther.message);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    return 0;
-  }
-
-  public _Fields fieldForId(int fieldId) {
-    return _Fields.findByThriftId(fieldId);
-  }
-
-  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-  }
-
-  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("LogEntry(");
-    boolean first = true;
-
-    sb.append("category:");
-    if (this.category == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.category);
-    }
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("message:");
-    if (this.message == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.message);
-    }
-    first = false;
-    sb.append(")");
-    return sb.toString();
-  }
-
-  public void validate() throws org.apache.thrift.TException {
-    // check for required fields
-    // check for sub-struct validity
-  }
-
-  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-    try {
-      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-    try {
-      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private static class LogEntryStandardSchemeFactory implements SchemeFactory {
-    public LogEntryStandardScheme getScheme() {
-      return new LogEntryStandardScheme();
-    }
-  }
-
-  private static class LogEntryStandardScheme extends StandardScheme<LogEntry> {
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot, LogEntry struct) throws org.apache.thrift.TException {
-      org.apache.thrift.protocol.TField schemeField;
-      iprot.readStructBegin();
-      while (true)
-      {
-        schemeField = iprot.readFieldBegin();
-        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-          break;
-        }
-        switch (schemeField.id) {
-          case 1: // CATEGORY
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.category = iprot.readString();
-              struct.setCategoryIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 2: // MESSAGE
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.message = iprot.readString();
-              struct.setMessageIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          default:
-            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-        }
-        iprot.readFieldEnd();
-      }
-      iprot.readStructEnd();
-
-      // check for required fields of primitive type, which can't be checked in the validate method
-      struct.validate();
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot, LogEntry struct) throws org.apache.thrift.TException {
-      struct.validate();
-
-      oprot.writeStructBegin(STRUCT_DESC);
-      if (struct.category != null) {
-        oprot.writeFieldBegin(CATEGORY_FIELD_DESC);
-        oprot.writeString(struct.category);
-        oprot.writeFieldEnd();
-      }
-      if (struct.message != null) {
-        oprot.writeFieldBegin(MESSAGE_FIELD_DESC);
-        oprot.writeString(struct.message);
-        oprot.writeFieldEnd();
-      }
-      oprot.writeFieldStop();
-      oprot.writeStructEnd();
-    }
-
-  }
-
-  private static class LogEntryTupleSchemeFactory implements SchemeFactory {
-    public LogEntryTupleScheme getScheme() {
-      return new LogEntryTupleScheme();
-    }
-  }
-
-  private static class LogEntryTupleScheme extends TupleScheme<LogEntry> {
-
-    @Override
-    public void write(org.apache.thrift.protocol.TProtocol prot, LogEntry struct) throws org.apache.thrift.TException {
-      TTupleProtocol oprot = (TTupleProtocol) prot;
-      BitSet optionals = new BitSet();
-      if (struct.isSetCategory()) {
-        optionals.set(0);
-      }
-      if (struct.isSetMessage()) {
-        optionals.set(1);
-      }
-      oprot.writeBitSet(optionals, 2);
-      if (struct.isSetCategory()) {
-        oprot.writeString(struct.category);
-      }
-      if (struct.isSetMessage()) {
-        oprot.writeString(struct.message);
-      }
-    }
-
-    @Override
-    public void read(org.apache.thrift.protocol.TProtocol prot, LogEntry struct) throws org.apache.thrift.TException {
-      TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(2);
-      if (incoming.get(0)) {
-        struct.category = iprot.readString();
-        struct.setCategoryIsSet(true);
-      }
-      if (incoming.get(1)) {
-        struct.message = iprot.readString();
-        struct.setMessageIsSet(true);
-      }
-    }
-  }
-
-}
-
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/ResultCode.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/ResultCode.java
deleted file mode 100644
index d0fc5ae..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/ResultCode.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-
-import java.util.Map;
-import java.util.HashMap;
-import org.apache.thrift.TEnum;
-
-public enum ResultCode implements org.apache.thrift.TEnum {
-  OK(0),
-  TRY_LATER(1);
-
-  private final int value;
-
-  private ResultCode(int value) {
-    this.value = value;
-  }
-
-  /**
-   * Get the integer value of this enum value, as defined in the Thrift IDL.
-   */
-  public int getValue() {
-    return value;
-  }
-
-  /**
-   * Find a the enum type by its integer value, as defined in the Thrift IDL.
-   * @return null if the value is not found.
-   */
-  public static ResultCode findByValue(int value) { 
-    switch (value) {
-      case 0:
-        return OK;
-      case 1:
-        return TRY_LATER;
-      default:
-        return null;
-    }
-  }
-}
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Scribe.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Scribe.java
deleted file mode 100644
index 7e51998..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Scribe.java
+++ /dev/null
@@ -1,957 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class Scribe {
-
-  public interface Iface {
-
-    public ResultCode Log(List<LogEntry> messages) throws org.apache.thrift.TException;
-
-  }
-
-  public interface AsyncIface {
-
-    public void Log(List<LogEntry> messages, org.apache.thrift.async.AsyncMethodCallback<AsyncClient.Log_call> resultHandler) throws org.apache.thrift.TException;
-
-  }
-
-  public static class Client extends org.apache.thrift.TServiceClient implements Iface {
-    public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {
-      public Factory() {}
-      public Client getClient(org.apache.thrift.protocol.TProtocol prot) {
-        return new Client(prot);
-      }
-      public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
-        return new Client(iprot, oprot);
-      }
-    }
-
-    public Client(org.apache.thrift.protocol.TProtocol prot)
-    {
-      super(prot, prot);
-    }
-
-    public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
-      super(iprot, oprot);
-    }
-
-    public ResultCode Log(List<LogEntry> messages) throws org.apache.thrift.TException
-    {
-      send_Log(messages);
-      return recv_Log();
-    }
-
-    public void send_Log(List<LogEntry> messages) throws org.apache.thrift.TException
-    {
-      Log_args args = new Log_args();
-      args.setMessages(messages);
-      sendBase("Log", args);
-    }
-
-    public ResultCode recv_Log() throws org.apache.thrift.TException
-    {
-      Log_result result = new Log_result();
-      receiveBase(result, "Log");
-      if (result.isSetSuccess()) {
-        return result.success;
-      }
-      throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "Log failed: unknown result");
-    }
-
-  }
-  public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface {
-    public static class Factory implements org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {
-      private org.apache.thrift.async.TAsyncClientManager clientManager;
-      private org.apache.thrift.protocol.TProtocolFactory protocolFactory;
-      public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) {
-        this.clientManager = clientManager;
-        this.protocolFactory = protocolFactory;
-      }
-      public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) {
-        return new AsyncClient(protocolFactory, clientManager, transport);
-      }
-    }
-
-    public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) {
-      super(protocolFactory, clientManager, transport);
-    }
-
-    public void Log(List<LogEntry> messages, org.apache.thrift.async.AsyncMethodCallback<Log_call> resultHandler) throws org.apache.thrift.TException {
-      checkReady();
-      Log_call method_call = new Log_call(messages, resultHandler, this, ___protocolFactory, ___transport);
-      this.___currentMethod = method_call;
-      ___manager.call(method_call);
-    }
-
-    public static class Log_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private List<LogEntry> messages;
-      public Log_call(List<LogEntry> messages, org.apache.thrift.async.AsyncMethodCallback<Log_call> resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
-        super(client, protocolFactory, transport, resultHandler, false);
-        this.messages = messages;
-      }
-
-      public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
-        prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("Log", org.apache.thrift.protocol.TMessageType.CALL, 0));
-        Log_args args = new Log_args();
-        args.setMessages(messages);
-        args.write(prot);
-        prot.writeMessageEnd();
-      }
-
-      public ResultCode getResult() throws org.apache.thrift.TException {
-        if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) {
-          throw new IllegalStateException("Method call not finished!");
-        }
-        org.apache.thrift.transport.TMemoryInputTransport memoryTransport = new org.apache.thrift.transport.TMemoryInputTransport(getFrameBuffer().array());
-        org.apache.thrift.protocol.TProtocol prot = client.getProtocolFactory().getProtocol(memoryTransport);
-        return (new Client(prot)).recv_Log();
-      }
-    }
-
-  }
-
-  public static class Processor<I extends Iface> extends org.apache.thrift.TBaseProcessor<I> implements org.apache.thrift.TProcessor {
-    private static final Logger LOGGER = LoggerFactory.getLogger(Processor.class.getName());
-    public Processor(I iface) {
-      super(iface, getProcessMap(new HashMap<String, org.apache.thrift.ProcessFunction<I, ? extends org.apache.thrift.TBase>>()));
-    }
-
-    protected Processor(I iface, Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> processMap) {
-      super(iface, getProcessMap(processMap));
-    }
-
-    private static <I extends Iface> Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> getProcessMap(Map<String,  org.apache.thrift.ProcessFunction<I, ? extends  org.apache.thrift.TBase>> processMap) {
-      processMap.put("Log", new Log());
-      return processMap;
-    }
-
-    public static class Log<I extends Iface> extends org.apache.thrift.ProcessFunction<I, Log_args> {
-      public Log() {
-        super("Log");
-      }
-
-      public Log_args getEmptyArgsInstance() {
-        return new Log_args();
-      }
-
-      protected boolean isOneway() {
-        return false;
-      }
-
-      public Log_result getResult(I iface, Log_args args) throws org.apache.thrift.TException {
-        Log_result result = new Log_result();
-        result.success = iface.Log(args.messages);
-        return result;
-      }
-    }
-
-  }
-
-  public static class Log_args implements org.apache.thrift.TBase<Log_args, Log_args._Fields>, java.io.Serializable, Cloneable   {
-    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Log_args");
-
-    private static final org.apache.thrift.protocol.TField MESSAGES_FIELD_DESC = new org.apache.thrift.protocol.TField("messages", org.apache.thrift.protocol.TType.LIST, (short)1);
-
-    private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-    static {
-      schemes.put(StandardScheme.class, new Log_argsStandardSchemeFactory());
-      schemes.put(TupleScheme.class, new Log_argsTupleSchemeFactory());
-    }
-
-    public List<LogEntry> messages; // required
-
-    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-      MESSAGES((short)1, "messages");
-
-      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-      static {
-        for (_Fields field : EnumSet.allOf(_Fields.class)) {
-          byName.put(field.getFieldName(), field);
-        }
-      }
-
-      /**
-       * Find the _Fields constant that matches fieldId, or null if its not found.
-       */
-      public static _Fields findByThriftId(int fieldId) {
-        switch(fieldId) {
-          case 1: // MESSAGES
-            return MESSAGES;
-          default:
-            return null;
-        }
-      }
-
-      /**
-       * Find the _Fields constant that matches fieldId, throwing an exception
-       * if it is not found.
-       */
-      public static _Fields findByThriftIdOrThrow(int fieldId) {
-        _Fields fields = findByThriftId(fieldId);
-        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-        return fields;
-      }
-
-      /**
-       * Find the _Fields constant that matches name, or null if its not found.
-       */
-      public static _Fields findByName(String name) {
-        return byName.get(name);
-      }
-
-      private final short _thriftId;
-      private final String _fieldName;
-
-      _Fields(short thriftId, String fieldName) {
-        _thriftId = thriftId;
-        _fieldName = fieldName;
-      }
-
-      public short getThriftFieldId() {
-        return _thriftId;
-      }
-
-      public String getFieldName() {
-        return _fieldName;
-      }
-    }
-
-    // isset id assignments
-    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-    static {
-      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-      tmpMap.put(_Fields.MESSAGES, new org.apache.thrift.meta_data.FieldMetaData("messages", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-          new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, 
-              new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, LogEntry.class))));
-      metaDataMap = Collections.unmodifiableMap(tmpMap);
-      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Log_args.class, metaDataMap);
-    }
-
-    public Log_args() {
-    }
-
-    public Log_args(
-      List<LogEntry> messages)
-    {
-      this();
-      this.messages = messages;
-    }
-
-    /**
-     * Performs a deep copy on <i>other</i>.
-     */
-    public Log_args(Log_args other) {
-      if (other.isSetMessages()) {
-        List<LogEntry> __this__messages = new ArrayList<LogEntry>();
-        for (LogEntry other_element : other.messages) {
-          __this__messages.add(new LogEntry(other_element));
-        }
-        this.messages = __this__messages;
-      }
-    }
-
-    public Log_args deepCopy() {
-      return new Log_args(this);
-    }
-
-    @Override
-    public void clear() {
-      this.messages = null;
-    }
-
-    public int getMessagesSize() {
-      return (this.messages == null) ? 0 : this.messages.size();
-    }
-
-    public java.util.Iterator<LogEntry> getMessagesIterator() {
-      return (this.messages == null) ? null : this.messages.iterator();
-    }
-
-    public void addToMessages(LogEntry elem) {
-      if (this.messages == null) {
-        this.messages = new ArrayList<LogEntry>();
-      }
-      this.messages.add(elem);
-    }
-
-    public List<LogEntry> getMessages() {
-      return this.messages;
-    }
-
-    public Log_args setMessages(List<LogEntry> messages) {
-      this.messages = messages;
-      return this;
-    }
-
-    public void unsetMessages() {
-      this.messages = null;
-    }
-
-    /** Returns true if field messages is set (has been assigned a value) and false otherwise */
-    public boolean isSetMessages() {
-      return this.messages != null;
-    }
-
-    public void setMessagesIsSet(boolean value) {
-      if (!value) {
-        this.messages = null;
-      }
-    }
-
-    public void setFieldValue(_Fields field, Object value) {
-      switch (field) {
-      case MESSAGES:
-        if (value == null) {
-          unsetMessages();
-        } else {
-          setMessages((List<LogEntry>)value);
-        }
-        break;
-
-      }
-    }
-
-    public Object getFieldValue(_Fields field) {
-      switch (field) {
-      case MESSAGES:
-        return getMessages();
-
-      }
-      throw new IllegalStateException();
-    }
-
-    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-    public boolean isSet(_Fields field) {
-      if (field == null) {
-        throw new IllegalArgumentException();
-      }
-
-      switch (field) {
-      case MESSAGES:
-        return isSetMessages();
-      }
-      throw new IllegalStateException();
-    }
-
-    @Override
-    public boolean equals(Object that) {
-      if (that == null)
-        return false;
-      if (that instanceof Log_args)
-        return this.equals((Log_args)that);
-      return false;
-    }
-
-    public boolean equals(Log_args that) {
-      if (that == null)
-        return false;
-
-      boolean this_present_messages = true && this.isSetMessages();
-      boolean that_present_messages = true && that.isSetMessages();
-      if (this_present_messages || that_present_messages) {
-        if (!(this_present_messages && that_present_messages))
-          return false;
-        if (!this.messages.equals(that.messages))
-          return false;
-      }
-
-      return true;
-    }
-
-    @Override
-    public int hashCode() {
-      return 0;
-    }
-
-    public int compareTo(Log_args other) {
-      if (!getClass().equals(other.getClass())) {
-        return getClass().getName().compareTo(other.getClass().getName());
-      }
-
-      int lastComparison = 0;
-      Log_args typedOther = (Log_args)other;
-
-      lastComparison = Boolean.valueOf(isSetMessages()).compareTo(typedOther.isSetMessages());
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-      if (isSetMessages()) {
-        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.messages, typedOther.messages);
-        if (lastComparison != 0) {
-          return lastComparison;
-        }
-      }
-      return 0;
-    }
-
-    public _Fields fieldForId(int fieldId) {
-      return _Fields.findByThriftId(fieldId);
-    }
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-      schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-      schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder sb = new StringBuilder("Log_args(");
-      boolean first = true;
-
-      sb.append("messages:");
-      if (this.messages == null) {
-        sb.append("null");
-      } else {
-        sb.append(this.messages);
-      }
-      first = false;
-      sb.append(")");
-      return sb.toString();
-    }
-
-    public void validate() throws org.apache.thrift.TException {
-      // check for required fields
-      // check for sub-struct validity
-    }
-
-    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-      try {
-        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-      } catch (org.apache.thrift.TException te) {
-        throw new java.io.IOException(te);
-      }
-    }
-
-    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-      try {
-        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-      } catch (org.apache.thrift.TException te) {
-        throw new java.io.IOException(te);
-      }
-    }
-
-    private static class Log_argsStandardSchemeFactory implements SchemeFactory {
-      public Log_argsStandardScheme getScheme() {
-        return new Log_argsStandardScheme();
-      }
-    }
-
-    private static class Log_argsStandardScheme extends StandardScheme<Log_args> {
-
-      public void read(org.apache.thrift.protocol.TProtocol iprot, Log_args struct) throws org.apache.thrift.TException {
-        org.apache.thrift.protocol.TField schemeField;
-        iprot.readStructBegin();
-        while (true)
-        {
-          schemeField = iprot.readFieldBegin();
-          if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-            break;
-          }
-          switch (schemeField.id) {
-            case 1: // MESSAGES
-              if (schemeField.type == org.apache.thrift.protocol.TType.LIST) {
-                {
-                  org.apache.thrift.protocol.TList _list0 = iprot.readListBegin();
-                  struct.messages = new ArrayList<LogEntry>(_list0.size);
-                  for (int _i1 = 0; _i1 < _list0.size; ++_i1)
-                  {
-                    LogEntry _elem2; // required
-                    _elem2 = new LogEntry();
-                    _elem2.read(iprot);
-                    struct.messages.add(_elem2);
-                  }
-                  iprot.readListEnd();
-                }
-                struct.setMessagesIsSet(true);
-              } else { 
-                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-              }
-              break;
-            default:
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-          }
-          iprot.readFieldEnd();
-        }
-        iprot.readStructEnd();
-
-        // check for required fields of primitive type, which can't be checked in the validate method
-        struct.validate();
-      }
-
-      public void write(org.apache.thrift.protocol.TProtocol oprot, Log_args struct) throws org.apache.thrift.TException {
-        struct.validate();
-
-        oprot.writeStructBegin(STRUCT_DESC);
-        if (struct.messages != null) {
-          oprot.writeFieldBegin(MESSAGES_FIELD_DESC);
-          {
-            oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, struct.messages.size()));
-            for (LogEntry _iter3 : struct.messages)
-            {
-              _iter3.write(oprot);
-            }
-            oprot.writeListEnd();
-          }
-          oprot.writeFieldEnd();
-        }
-        oprot.writeFieldStop();
-        oprot.writeStructEnd();
-      }
-
-    }
-
-    private static class Log_argsTupleSchemeFactory implements SchemeFactory {
-      public Log_argsTupleScheme getScheme() {
-        return new Log_argsTupleScheme();
-      }
-    }
-
-    private static class Log_argsTupleScheme extends TupleScheme<Log_args> {
-
-      @Override
-      public void write(org.apache.thrift.protocol.TProtocol prot, Log_args struct) throws org.apache.thrift.TException {
-        TTupleProtocol oprot = (TTupleProtocol) prot;
-        BitSet optionals = new BitSet();
-        if (struct.isSetMessages()) {
-          optionals.set(0);
-        }
-        oprot.writeBitSet(optionals, 1);
-        if (struct.isSetMessages()) {
-          {
-            oprot.writeI32(struct.messages.size());
-            for (LogEntry _iter4 : struct.messages)
-            {
-              _iter4.write(oprot);
-            }
-          }
-        }
-      }
-
-      @Override
-      public void read(org.apache.thrift.protocol.TProtocol prot, Log_args struct) throws org.apache.thrift.TException {
-        TTupleProtocol iprot = (TTupleProtocol) prot;
-        BitSet incoming = iprot.readBitSet(1);
-        if (incoming.get(0)) {
-          {
-            org.apache.thrift.protocol.TList _list5 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
-            struct.messages = new ArrayList<LogEntry>(_list5.size);
-            for (int _i6 = 0; _i6 < _list5.size; ++_i6)
-            {
-              LogEntry _elem7; // required
-              _elem7 = new LogEntry();
-              _elem7.read(iprot);
-              struct.messages.add(_elem7);
-            }
-          }
-          struct.setMessagesIsSet(true);
-        }
-      }
-    }
-
-  }
-
-  public static class Log_result implements org.apache.thrift.TBase<Log_result, Log_result._Fields>, java.io.Serializable, Cloneable   {
-    private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Log_result");
-
-    private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.I32, (short)0);
-
-    private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-    static {
-      schemes.put(StandardScheme.class, new Log_resultStandardSchemeFactory());
-      schemes.put(TupleScheme.class, new Log_resultTupleSchemeFactory());
-    }
-
-    /**
-     * 
-     * @see ResultCode
-     */
-    public ResultCode success; // required
-
-    /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-    public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-      /**
-       * 
-       * @see ResultCode
-       */
-      SUCCESS((short)0, "success");
-
-      private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-      static {
-        for (_Fields field : EnumSet.allOf(_Fields.class)) {
-          byName.put(field.getFieldName(), field);
-        }
-      }
-
-      /**
-       * Find the _Fields constant that matches fieldId, or null if its not found.
-       */
-      public static _Fields findByThriftId(int fieldId) {
-        switch(fieldId) {
-          case 0: // SUCCESS
-            return SUCCESS;
-          default:
-            return null;
-        }
-      }
-
-      /**
-       * Find the _Fields constant that matches fieldId, throwing an exception
-       * if it is not found.
-       */
-      public static _Fields findByThriftIdOrThrow(int fieldId) {
-        _Fields fields = findByThriftId(fieldId);
-        if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-        return fields;
-      }
-
-      /**
-       * Find the _Fields constant that matches name, or null if its not found.
-       */
-      public static _Fields findByName(String name) {
-        return byName.get(name);
-      }
-
-      private final short _thriftId;
-      private final String _fieldName;
-
-      _Fields(short thriftId, String fieldName) {
-        _thriftId = thriftId;
-        _fieldName = fieldName;
-      }
-
-      public short getThriftFieldId() {
-        return _thriftId;
-      }
-
-      public String getFieldName() {
-        return _fieldName;
-      }
-    }
-
-    // isset id assignments
-    public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-    static {
-      Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-      tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-          new org.apache.thrift.meta_data.EnumMetaData(org.apache.thrift.protocol.TType.ENUM, ResultCode.class)));
-      metaDataMap = Collections.unmodifiableMap(tmpMap);
-      org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Log_result.class, metaDataMap);
-    }
-
-    public Log_result() {
-    }
-
-    public Log_result(
-      ResultCode success)
-    {
-      this();
-      this.success = success;
-    }
-
-    /**
-     * Performs a deep copy on <i>other</i>.
-     */
-    public Log_result(Log_result other) {
-      if (other.isSetSuccess()) {
-        this.success = other.success;
-      }
-    }
-
-    public Log_result deepCopy() {
-      return new Log_result(this);
-    }
-
-    @Override
-    public void clear() {
-      this.success = null;
-    }
-
-    /**
-     * 
-     * @see ResultCode
-     */
-    public ResultCode getSuccess() {
-      return this.success;
-    }
-
-    /**
-     * 
-     * @see ResultCode
-     */
-    public Log_result setSuccess(ResultCode success) {
-      this.success = success;
-      return this;
-    }
-
-    public void unsetSuccess() {
-      this.success = null;
-    }
-
-    /** Returns true if field success is set (has been assigned a value) and false otherwise */
-    public boolean isSetSuccess() {
-      return this.success != null;
-    }
-
-    public void setSuccessIsSet(boolean value) {
-      if (!value) {
-        this.success = null;
-      }
-    }
-
-    public void setFieldValue(_Fields field, Object value) {
-      switch (field) {
-      case SUCCESS:
-        if (value == null) {
-          unsetSuccess();
-        } else {
-          setSuccess((ResultCode)value);
-        }
-        break;
-
-      }
-    }
-
-    public Object getFieldValue(_Fields field) {
-      switch (field) {
-      case SUCCESS:
-        return getSuccess();
-
-      }
-      throw new IllegalStateException();
-    }
-
-    /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-    public boolean isSet(_Fields field) {
-      if (field == null) {
-        throw new IllegalArgumentException();
-      }
-
-      switch (field) {
-      case SUCCESS:
-        return isSetSuccess();
-      }
-      throw new IllegalStateException();
-    }
-
-    @Override
-    public boolean equals(Object that) {
-      if (that == null)
-        return false;
-      if (that instanceof Log_result)
-        return this.equals((Log_result)that);
-      return false;
-    }
-
-    public boolean equals(Log_result that) {
-      if (that == null)
-        return false;
-
-      boolean this_present_success = true && this.isSetSuccess();
-      boolean that_present_success = true && that.isSetSuccess();
-      if (this_present_success || that_present_success) {
-        if (!(this_present_success && that_present_success))
-          return false;
-        if (!this.success.equals(that.success))
-          return false;
-      }
-
-      return true;
-    }
-
-    @Override
-    public int hashCode() {
-      return 0;
-    }
-
-    public int compareTo(Log_result other) {
-      if (!getClass().equals(other.getClass())) {
-        return getClass().getName().compareTo(other.getClass().getName());
-      }
-
-      int lastComparison = 0;
-      Log_result typedOther = (Log_result)other;
-
-      lastComparison = Boolean.valueOf(isSetSuccess()).compareTo(typedOther.isSetSuccess());
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-      if (isSetSuccess()) {
-        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.success, typedOther.success);
-        if (lastComparison != 0) {
-          return lastComparison;
-        }
-      }
-      return 0;
-    }
-
-    public _Fields fieldForId(int fieldId) {
-      return _Fields.findByThriftId(fieldId);
-    }
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-      schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-      schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-      }
-
-    @Override
-    public String toString() {
-      StringBuilder sb = new StringBuilder("Log_result(");
-      boolean first = true;
-
-      sb.append("success:");
-      if (this.success == null) {
-        sb.append("null");
-      } else {
-        sb.append(this.success);
-      }
-      first = false;
-      sb.append(")");
-      return sb.toString();
-    }
-
-    public void validate() throws org.apache.thrift.TException {
-      // check for required fields
-      // check for sub-struct validity
-    }
-
-    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-      try {
-        write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-      } catch (org.apache.thrift.TException te) {
-        throw new java.io.IOException(te);
-      }
-    }
-
-    private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-      try {
-        read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-      } catch (org.apache.thrift.TException te) {
-        throw new java.io.IOException(te);
-      }
-    }
-
-    private static class Log_resultStandardSchemeFactory implements SchemeFactory {
-      public Log_resultStandardScheme getScheme() {
-        return new Log_resultStandardScheme();
-      }
-    }
-
-    private static class Log_resultStandardScheme extends StandardScheme<Log_result> {
-
-      public void read(org.apache.thrift.protocol.TProtocol iprot, Log_result struct) throws org.apache.thrift.TException {
-        org.apache.thrift.protocol.TField schemeField;
-        iprot.readStructBegin();
-        while (true)
-        {
-          schemeField = iprot.readFieldBegin();
-          if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-            break;
-          }
-          switch (schemeField.id) {
-            case 0: // SUCCESS
-              if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
-                struct.success = ResultCode.findByValue(iprot.readI32());
-                struct.setSuccessIsSet(true);
-              } else { 
-                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-              }
-              break;
-            default:
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-          }
-          iprot.readFieldEnd();
-        }
-        iprot.readStructEnd();
-
-        // check for required fields of primitive type, which can't be checked in the validate method
-        struct.validate();
-      }
-
-      public void write(org.apache.thrift.protocol.TProtocol oprot, Log_result struct) throws org.apache.thrift.TException {
-        struct.validate();
-
-        oprot.writeStructBegin(STRUCT_DESC);
-        if (struct.success != null) {
-          oprot.writeFieldBegin(SUCCESS_FIELD_DESC);
-          oprot.writeI32(struct.success.getValue());
-          oprot.writeFieldEnd();
-        }
-        oprot.writeFieldStop();
-        oprot.writeStructEnd();
-      }
-
-    }
-
-    private static class Log_resultTupleSchemeFactory implements SchemeFactory {
-      public Log_resultTupleScheme getScheme() {
-        return new Log_resultTupleScheme();
-      }
-    }
-
-    private static class Log_resultTupleScheme extends TupleScheme<Log_result> {
-
-      @Override
-      public void write(org.apache.thrift.protocol.TProtocol prot, Log_result struct) throws org.apache.thrift.TException {
-        TTupleProtocol oprot = (TTupleProtocol) prot;
-        BitSet optionals = new BitSet();
-        if (struct.isSetSuccess()) {
-          optionals.set(0);
-        }
-        oprot.writeBitSet(optionals, 1);
-        if (struct.isSetSuccess()) {
-          oprot.writeI32(struct.success.getValue());
-        }
-      }
-
-      @Override
-      public void read(org.apache.thrift.protocol.TProtocol prot, Log_result struct) throws org.apache.thrift.TException {
-        TTupleProtocol iprot = (TTupleProtocol) prot;
-        BitSet incoming = iprot.readBitSet(1);
-        if (incoming.get(0)) {
-          struct.success = ResultCode.findByValue(iprot.readI32());
-          struct.setSuccessIsSet(true);
-        }
-      }
-    }
-
-  }
-
-}
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Span.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Span.java
deleted file mode 100644
index ef6ae04..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/Span.java
+++ /dev/null
@@ -1,1079 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class Span implements org.apache.thrift.TBase<Span, Span._Fields>, java.io.Serializable, Cloneable {
-  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Span");
-
-  private static final org.apache.thrift.protocol.TField TRACE_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("trace_id", org.apache.thrift.protocol.TType.I64, (short)1);
-  private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)3);
-  private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I64, (short)4);
-  private static final org.apache.thrift.protocol.TField PARENT_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("parent_id", org.apache.thrift.protocol.TType.I64, (short)5);
-  private static final org.apache.thrift.protocol.TField ANNOTATIONS_FIELD_DESC = new org.apache.thrift.protocol.TField("annotations", org.apache.thrift.protocol.TType.LIST, (short)6);
-  private static final org.apache.thrift.protocol.TField BINARY_ANNOTATIONS_FIELD_DESC = new org.apache.thrift.protocol.TField("binary_annotations", org.apache.thrift.protocol.TType.LIST, (short)8);
-  private static final org.apache.thrift.protocol.TField DEBUG_FIELD_DESC = new org.apache.thrift.protocol.TField("debug", org.apache.thrift.protocol.TType.BOOL, (short)9);
-
-  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
-  static {
-    schemes.put(StandardScheme.class, new SpanStandardSchemeFactory());
-    schemes.put(TupleScheme.class, new SpanTupleSchemeFactory());
-  }
-
-  public long trace_id; // required
-  public String name; // required
-  public long id; // required
-  public long parent_id; // optional
-  public List<Annotation> annotations; // required
-  public List<BinaryAnnotation> binary_annotations; // required
-  public boolean debug; // optional
-
-  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
-  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
-    TRACE_ID((short)1, "trace_id"),
-    NAME((short)3, "name"),
-    ID((short)4, "id"),
-    PARENT_ID((short)5, "parent_id"),
-    ANNOTATIONS((short)6, "annotations"),
-    BINARY_ANNOTATIONS((short)8, "binary_annotations"),
-    DEBUG((short)9, "debug");
-
-    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
-
-    static {
-      for (_Fields field : EnumSet.allOf(_Fields.class)) {
-        byName.put(field.getFieldName(), field);
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, or null if its not found.
-     */
-    public static _Fields findByThriftId(int fieldId) {
-      switch(fieldId) {
-        case 1: // TRACE_ID
-          return TRACE_ID;
-        case 3: // NAME
-          return NAME;
-        case 4: // ID
-          return ID;
-        case 5: // PARENT_ID
-          return PARENT_ID;
-        case 6: // ANNOTATIONS
-          return ANNOTATIONS;
-        case 8: // BINARY_ANNOTATIONS
-          return BINARY_ANNOTATIONS;
-        case 9: // DEBUG
-          return DEBUG;
-        default:
-          return null;
-      }
-    }
-
-    /**
-     * Find the _Fields constant that matches fieldId, throwing an exception
-     * if it is not found.
-     */
-    public static _Fields findByThriftIdOrThrow(int fieldId) {
-      _Fields fields = findByThriftId(fieldId);
-      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
-      return fields;
-    }
-
-    /**
-     * Find the _Fields constant that matches name, or null if its not found.
-     */
-    public static _Fields findByName(String name) {
-      return byName.get(name);
-    }
-
-    private final short _thriftId;
-    private final String _fieldName;
-
-    _Fields(short thriftId, String fieldName) {
-      _thriftId = thriftId;
-      _fieldName = fieldName;
-    }
-
-    public short getThriftFieldId() {
-      return _thriftId;
-    }
-
-    public String getFieldName() {
-      return _fieldName;
-    }
-  }
-
-  // isset id assignments
-  private static final int __TRACE_ID_ISSET_ID = 0;
-  private static final int __ID_ISSET_ID = 1;
-  private static final int __PARENT_ID_ISSET_ID = 2;
-  private static final int __DEBUG_ISSET_ID = 3;
-  private byte __isset_bitfield = 0;
-  private _Fields optionals[] = {_Fields.PARENT_ID,_Fields.DEBUG};
-  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
-  static {
-    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
-    tmpMap.put(_Fields.TRACE_ID, new org.apache.thrift.meta_data.FieldMetaData("trace_id", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64)));
-    tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64)));
-    tmpMap.put(_Fields.PARENT_ID, new org.apache.thrift.meta_data.FieldMetaData("parent_id", org.apache.thrift.TFieldRequirementType.OPTIONAL, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64)));
-    tmpMap.put(_Fields.ANNOTATIONS, new org.apache.thrift.meta_data.FieldMetaData("annotations", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, 
-            new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, Annotation.class))));
-    tmpMap.put(_Fields.BINARY_ANNOTATIONS, new org.apache.thrift.meta_data.FieldMetaData("binary_annotations", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, 
-            new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, BinaryAnnotation.class))));
-    tmpMap.put(_Fields.DEBUG, new org.apache.thrift.meta_data.FieldMetaData("debug", org.apache.thrift.TFieldRequirementType.OPTIONAL, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.BOOL)));
-    metaDataMap = Collections.unmodifiableMap(tmpMap);
-    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Span.class, metaDataMap);
-  }
-
-  public Span() {
-    this.debug = false;
-
-  }
-
-  public Span(
-    long trace_id,
-    String name,
-    long id,
-    List<Annotation> annotations,
-    List<BinaryAnnotation> binary_annotations)
-  {
-    this();
-    this.trace_id = trace_id;
-    setTrace_idIsSet(true);
-    this.name = name;
-    this.id = id;
-    setIdIsSet(true);
-    this.annotations = annotations;
-    this.binary_annotations = binary_annotations;
-  }
-
-  /**
-   * Performs a deep copy on <i>other</i>.
-   */
-  public Span(Span other) {
-    __isset_bitfield = other.__isset_bitfield;
-    this.trace_id = other.trace_id;
-    if (other.isSetName()) {
-      this.name = other.name;
-    }
-    this.id = other.id;
-    this.parent_id = other.parent_id;
-    if (other.isSetAnnotations()) {
-      List<Annotation> __this__annotations = new ArrayList<Annotation>();
-      for (Annotation other_element : other.annotations) {
-        __this__annotations.add(new Annotation(other_element));
-      }
-      this.annotations = __this__annotations;
-    }
-    if (other.isSetBinary_annotations()) {
-      List<BinaryAnnotation> __this__binary_annotations = new ArrayList<BinaryAnnotation>();
-      for (BinaryAnnotation other_element : other.binary_annotations) {
-        __this__binary_annotations.add(new BinaryAnnotation(other_element));
-      }
-      this.binary_annotations = __this__binary_annotations;
-    }
-    this.debug = other.debug;
-  }
-
-  public Span deepCopy() {
-    return new Span(this);
-  }
-
-  @Override
-  public void clear() {
-    setTrace_idIsSet(false);
-    this.trace_id = 0;
-    this.name = null;
-    setIdIsSet(false);
-    this.id = 0;
-    setParent_idIsSet(false);
-    this.parent_id = 0;
-    this.annotations = null;
-    this.binary_annotations = null;
-    this.debug = false;
-
-  }
-
-  public long getTrace_id() {
-    return this.trace_id;
-  }
-
-  public Span setTrace_id(long trace_id) {
-    this.trace_id = trace_id;
-    setTrace_idIsSet(true);
-    return this;
-  }
-
-  public void unsetTrace_id() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __TRACE_ID_ISSET_ID);
-  }
-
-  /** Returns true if field trace_id is set (has been assigned a value) and false otherwise */
-  public boolean isSetTrace_id() {
-    return EncodingUtils.testBit(__isset_bitfield, __TRACE_ID_ISSET_ID);
-  }
-
-  public void setTrace_idIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __TRACE_ID_ISSET_ID, value);
-  }
-
-  public String getName() {
-    return this.name;
-  }
-
-  public Span setName(String name) {
-    this.name = name;
-    return this;
-  }
-
-  public void unsetName() {
-    this.name = null;
-  }
-
-  /** Returns true if field name is set (has been assigned a value) and false otherwise */
-  public boolean isSetName() {
-    return this.name != null;
-  }
-
-  public void setNameIsSet(boolean value) {
-    if (!value) {
-      this.name = null;
-    }
-  }
-
-  public long getId() {
-    return this.id;
-  }
-
-  public Span setId(long id) {
-    this.id = id;
-    setIdIsSet(true);
-    return this;
-  }
-
-  public void unsetId() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID);
-  }
-
-  /** Returns true if field id is set (has been assigned a value) and false otherwise */
-  public boolean isSetId() {
-    return EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID);
-  }
-
-  public void setIdIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value);
-  }
-
-  public long getParent_id() {
-    return this.parent_id;
-  }
-
-  public Span setParent_id(long parent_id) {
-    this.parent_id = parent_id;
-    setParent_idIsSet(true);
-    return this;
-  }
-
-  public void unsetParent_id() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __PARENT_ID_ISSET_ID);
-  }
-
-  /** Returns true if field parent_id is set (has been assigned a value) and false otherwise */
-  public boolean isSetParent_id() {
-    return EncodingUtils.testBit(__isset_bitfield, __PARENT_ID_ISSET_ID);
-  }
-
-  public void setParent_idIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __PARENT_ID_ISSET_ID, value);
-  }
-
-  public int getAnnotationsSize() {
-    return (this.annotations == null) ? 0 : this.annotations.size();
-  }
-
-  public java.util.Iterator<Annotation> getAnnotationsIterator() {
-    return (this.annotations == null) ? null : this.annotations.iterator();
-  }
-
-  public void addToAnnotations(Annotation elem) {
-    if (this.annotations == null) {
-      this.annotations = new ArrayList<Annotation>();
-    }
-    this.annotations.add(elem);
-  }
-
-  public List<Annotation> getAnnotations() {
-    return this.annotations;
-  }
-
-  public Span setAnnotations(List<Annotation> annotations) {
-    this.annotations = annotations;
-    return this;
-  }
-
-  public void unsetAnnotations() {
-    this.annotations = null;
-  }
-
-  /** Returns true if field annotations is set (has been assigned a value) and false otherwise */
-  public boolean isSetAnnotations() {
-    return this.annotations != null;
-  }
-
-  public void setAnnotationsIsSet(boolean value) {
-    if (!value) {
-      this.annotations = null;
-    }
-  }
-
-  public int getBinary_annotationsSize() {
-    return (this.binary_annotations == null) ? 0 : this.binary_annotations.size();
-  }
-
-  public java.util.Iterator<BinaryAnnotation> getBinary_annotationsIterator() {
-    return (this.binary_annotations == null) ? null : this.binary_annotations.iterator();
-  }
-
-  public void addToBinary_annotations(BinaryAnnotation elem) {
-    if (this.binary_annotations == null) {
-      this.binary_annotations = new ArrayList<BinaryAnnotation>();
-    }
-    this.binary_annotations.add(elem);
-  }
-
-  public List<BinaryAnnotation> getBinary_annotations() {
-    return this.binary_annotations;
-  }
-
-  public Span setBinary_annotations(List<BinaryAnnotation> binary_annotations) {
-    this.binary_annotations = binary_annotations;
-    return this;
-  }
-
-  public void unsetBinary_annotations() {
-    this.binary_annotations = null;
-  }
-
-  /** Returns true if field binary_annotations is set (has been assigned a value) and false otherwise */
-  public boolean isSetBinary_annotations() {
-    return this.binary_annotations != null;
-  }
-
-  public void setBinary_annotationsIsSet(boolean value) {
-    if (!value) {
-      this.binary_annotations = null;
-    }
-  }
-
-  public boolean isDebug() {
-    return this.debug;
-  }
-
-  public Span setDebug(boolean debug) {
-    this.debug = debug;
-    setDebugIsSet(true);
-    return this;
-  }
-
-  public void unsetDebug() {
-    __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __DEBUG_ISSET_ID);
-  }
-
-  /** Returns true if field debug is set (has been assigned a value) and false otherwise */
-  public boolean isSetDebug() {
-    return EncodingUtils.testBit(__isset_bitfield, __DEBUG_ISSET_ID);
-  }
-
-  public void setDebugIsSet(boolean value) {
-    __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __DEBUG_ISSET_ID, value);
-  }
-
-  public void setFieldValue(_Fields field, Object value) {
-    switch (field) {
-    case TRACE_ID:
-      if (value == null) {
-        unsetTrace_id();
-      } else {
-        setTrace_id((Long)value);
-      }
-      break;
-
-    case NAME:
-      if (value == null) {
-        unsetName();
-      } else {
-        setName((String)value);
-      }
-      break;
-
-    case ID:
-      if (value == null) {
-        unsetId();
-      } else {
-        setId((Long)value);
-      }
-      break;
-
-    case PARENT_ID:
-      if (value == null) {
-        unsetParent_id();
-      } else {
-        setParent_id((Long)value);
-      }
-      break;
-
-    case ANNOTATIONS:
-      if (value == null) {
-        unsetAnnotations();
-      } else {
-        setAnnotations((List<Annotation>)value);
-      }
-      break;
-
-    case BINARY_ANNOTATIONS:
-      if (value == null) {
-        unsetBinary_annotations();
-      } else {
-        setBinary_annotations((List<BinaryAnnotation>)value);
-      }
-      break;
-
-    case DEBUG:
-      if (value == null) {
-        unsetDebug();
-      } else {
-        setDebug((Boolean)value);
-      }
-      break;
-
-    }
-  }
-
-  public Object getFieldValue(_Fields field) {
-    switch (field) {
-    case TRACE_ID:
-      return Long.valueOf(getTrace_id());
-
-    case NAME:
-      return getName();
-
-    case ID:
-      return Long.valueOf(getId());
-
-    case PARENT_ID:
-      return Long.valueOf(getParent_id());
-
-    case ANNOTATIONS:
-      return getAnnotations();
-
-    case BINARY_ANNOTATIONS:
-      return getBinary_annotations();
-
-    case DEBUG:
-      return Boolean.valueOf(isDebug());
-
-    }
-    throw new IllegalStateException();
-  }
-
-  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
-  public boolean isSet(_Fields field) {
-    if (field == null) {
-      throw new IllegalArgumentException();
-    }
-
-    switch (field) {
-    case TRACE_ID:
-      return isSetTrace_id();
-    case NAME:
-      return isSetName();
-    case ID:
-      return isSetId();
-    case PARENT_ID:
-      return isSetParent_id();
-    case ANNOTATIONS:
-      return isSetAnnotations();
-    case BINARY_ANNOTATIONS:
-      return isSetBinary_annotations();
-    case DEBUG:
-      return isSetDebug();
-    }
-    throw new IllegalStateException();
-  }
-
-  @Override
-  public boolean equals(Object that) {
-    if (that == null)
-      return false;
-    if (that instanceof Span)
-      return this.equals((Span)that);
-    return false;
-  }
-
-  public boolean equals(Span that) {
-    if (that == null)
-      return false;
-
-    boolean this_present_trace_id = true;
-    boolean that_present_trace_id = true;
-    if (this_present_trace_id || that_present_trace_id) {
-      if (!(this_present_trace_id && that_present_trace_id))
-        return false;
-      if (this.trace_id != that.trace_id)
-        return false;
-    }
-
-    boolean this_present_name = true && this.isSetName();
-    boolean that_present_name = true && that.isSetName();
-    if (this_present_name || that_present_name) {
-      if (!(this_present_name && that_present_name))
-        return false;
-      if (!this.name.equals(that.name))
-        return false;
-    }
-
-    boolean this_present_id = true;
-    boolean that_present_id = true;
-    if (this_present_id || that_present_id) {
-      if (!(this_present_id && that_present_id))
-        return false;
-      if (this.id != that.id)
-        return false;
-    }
-
-    boolean this_present_parent_id = true && this.isSetParent_id();
-    boolean that_present_parent_id = true && that.isSetParent_id();
-    if (this_present_parent_id || that_present_parent_id) {
-      if (!(this_present_parent_id && that_present_parent_id))
-        return false;
-      if (this.parent_id != that.parent_id)
-        return false;
-    }
-
-    boolean this_present_annotations = true && this.isSetAnnotations();
-    boolean that_present_annotations = true && that.isSetAnnotations();
-    if (this_present_annotations || that_present_annotations) {
-      if (!(this_present_annotations && that_present_annotations))
-        return false;
-      if (!this.annotations.equals(that.annotations))
-        return false;
-    }
-
-    boolean this_present_binary_annotations = true && this.isSetBinary_annotations();
-    boolean that_present_binary_annotations = true && that.isSetBinary_annotations();
-    if (this_present_binary_annotations || that_present_binary_annotations) {
-      if (!(this_present_binary_annotations && that_present_binary_annotations))
-        return false;
-      if (!this.binary_annotations.equals(that.binary_annotations))
-        return false;
-    }
-
-    boolean this_present_debug = true && this.isSetDebug();
-    boolean that_present_debug = true && that.isSetDebug();
-    if (this_present_debug || that_present_debug) {
-      if (!(this_present_debug && that_present_debug))
-        return false;
-      if (this.debug != that.debug)
-        return false;
-    }
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    return 0;
-  }
-
-  public int compareTo(Span other) {
-    if (!getClass().equals(other.getClass())) {
-      return getClass().getName().compareTo(other.getClass().getName());
-    }
-
-    int lastComparison = 0;
-    Span typedOther = (Span)other;
-
-    lastComparison = Boolean.valueOf(isSetTrace_id()).compareTo(typedOther.isSetTrace_id());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetTrace_id()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.trace_id, typedOther.trace_id);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetName()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, typedOther.name);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetId()).compareTo(typedOther.isSetId());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetId()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, typedOther.id);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetParent_id()).compareTo(typedOther.isSetParent_id());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetParent_id()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.parent_id, typedOther.parent_id);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetAnnotations()).compareTo(typedOther.isSetAnnotations());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetAnnotations()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.annotations, typedOther.annotations);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetBinary_annotations()).compareTo(typedOther.isSetBinary_annotations());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetBinary_annotations()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.binary_annotations, typedOther.binary_annotations);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    lastComparison = Boolean.valueOf(isSetDebug()).compareTo(typedOther.isSetDebug());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetDebug()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.debug, typedOther.debug);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
-    return 0;
-  }
-
-  public _Fields fieldForId(int fieldId) {
-    return _Fields.findByThriftId(fieldId);
-  }
-
-  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
-    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
-  }
-
-  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
-    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder("Span(");
-    boolean first = true;
-
-    sb.append("trace_id:");
-    sb.append(this.trace_id);
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("name:");
-    if (this.name == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.name);
-    }
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("id:");
-    sb.append(this.id);
-    first = false;
-    if (isSetParent_id()) {
-      if (!first) sb.append(", ");
-      sb.append("parent_id:");
-      sb.append(this.parent_id);
-      first = false;
-    }
-    if (!first) sb.append(", ");
-    sb.append("annotations:");
-    if (this.annotations == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.annotations);
-    }
-    first = false;
-    if (!first) sb.append(", ");
-    sb.append("binary_annotations:");
-    if (this.binary_annotations == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.binary_annotations);
-    }
-    first = false;
-    if (isSetDebug()) {
-      if (!first) sb.append(", ");
-      sb.append("debug:");
-      sb.append(this.debug);
-      first = false;
-    }
-    sb.append(")");
-    return sb.toString();
-  }
-
-  public void validate() throws org.apache.thrift.TException {
-    // check for required fields
-    // check for sub-struct validity
-  }
-
-  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
-    try {
-      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
-    try {
-      // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
-      __isset_bitfield = 0;
-      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
-    } catch (org.apache.thrift.TException te) {
-      throw new java.io.IOException(te);
-    }
-  }
-
-  private static class SpanStandardSchemeFactory implements SchemeFactory {
-    public SpanStandardScheme getScheme() {
-      return new SpanStandardScheme();
-    }
-  }
-
-  private static class SpanStandardScheme extends StandardScheme<Span> {
-
-    public void read(org.apache.thrift.protocol.TProtocol iprot, Span struct) throws org.apache.thrift.TException {
-      org.apache.thrift.protocol.TField schemeField;
-      iprot.readStructBegin();
-      while (true)
-      {
-        schemeField = iprot.readFieldBegin();
-        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
-          break;
-        }
-        switch (schemeField.id) {
-          case 1: // TRACE_ID
-            if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
-              struct.trace_id = iprot.readI64();
-              struct.setTrace_idIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 3: // NAME
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.name = iprot.readString();
-              struct.setNameIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 4: // ID
-            if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
-              struct.id = iprot.readI64();
-              struct.setIdIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 5: // PARENT_ID
-            if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
-              struct.parent_id = iprot.readI64();
-              struct.setParent_idIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 6: // ANNOTATIONS
-            if (schemeField.type == org.apache.thrift.protocol.TType.LIST) {
-              {
-                org.apache.thrift.protocol.TList _list0 = iprot.readListBegin();
-                struct.annotations = new ArrayList<Annotation>(_list0.size);
-                for (int _i1 = 0; _i1 < _list0.size; ++_i1)
-                {
-                  Annotation _elem2; // required
-                  _elem2 = new Annotation();
-                  _elem2.read(iprot);
-                  struct.annotations.add(_elem2);
-                }
-                iprot.readListEnd();
-              }
-              struct.setAnnotationsIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 8: // BINARY_ANNOTATIONS
-            if (schemeField.type == org.apache.thrift.protocol.TType.LIST) {
-              {
-                org.apache.thrift.protocol.TList _list3 = iprot.readListBegin();
-                struct.binary_annotations = new ArrayList<BinaryAnnotation>(_list3.size);
-                for (int _i4 = 0; _i4 < _list3.size; ++_i4)
-                {
-                  BinaryAnnotation _elem5; // required
-                  _elem5 = new BinaryAnnotation();
-                  _elem5.read(iprot);
-                  struct.binary_annotations.add(_elem5);
-                }
-                iprot.readListEnd();
-              }
-              struct.setBinary_annotationsIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 9: // DEBUG
-            if (schemeField.type == org.apache.thrift.protocol.TType.BOOL) {
-              struct.debug = iprot.readBool();
-              struct.setDebugIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          default:
-            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-        }
-        iprot.readFieldEnd();
-      }
-      iprot.readStructEnd();
-
-      // check for required fields of primitive type, which can't be checked in the validate method
-      struct.validate();
-    }
-
-    public void write(org.apache.thrift.protocol.TProtocol oprot, Span struct) throws org.apache.thrift.TException {
-      struct.validate();
-
-      oprot.writeStructBegin(STRUCT_DESC);
-      oprot.writeFieldBegin(TRACE_ID_FIELD_DESC);
-      oprot.writeI64(struct.trace_id);
-      oprot.writeFieldEnd();
-      if (struct.name != null) {
-        oprot.writeFieldBegin(NAME_FIELD_DESC);
-        oprot.writeString(struct.name);
-        oprot.writeFieldEnd();
-      }
-      oprot.writeFieldBegin(ID_FIELD_DESC);
-      oprot.writeI64(struct.id);
-      oprot.writeFieldEnd();
-      if (struct.isSetParent_id()) {
-        oprot.writeFieldBegin(PARENT_ID_FIELD_DESC);
-        oprot.writeI64(struct.parent_id);
-        oprot.writeFieldEnd();
-      }
-      if (struct.annotations != null) {
-        oprot.writeFieldBegin(ANNOTATIONS_FIELD_DESC);
-        {
-          oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, struct.annotations.size()));
-          for (Annotation _iter6 : struct.annotations)
-          {
-            _iter6.write(oprot);
-          }
-          oprot.writeListEnd();
-        }
-        oprot.writeFieldEnd();
-      }
-      if (struct.binary_annotations != null) {
-        oprot.writeFieldBegin(BINARY_ANNOTATIONS_FIELD_DESC);
-        {
-          oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, struct.binary_annotations.size()));
-          for (BinaryAnnotation _iter7 : struct.binary_annotations)
-          {
-            _iter7.write(oprot);
-          }
-          oprot.writeListEnd();
-        }
-        oprot.writeFieldEnd();
-      }
-      if (struct.isSetDebug()) {
-        oprot.writeFieldBegin(DEBUG_FIELD_DESC);
-        oprot.writeBool(struct.debug);
-        oprot.writeFieldEnd();
-      }
-      oprot.writeFieldStop();
-      oprot.writeStructEnd();
-    }
-
-  }
-
-  private static class SpanTupleSchemeFactory implements SchemeFactory {
-    public SpanTupleScheme getScheme() {
-      return new SpanTupleScheme();
-    }
-  }
-
-  private static class SpanTupleScheme extends TupleScheme<Span> {
-
-    @Override
-    public void write(org.apache.thrift.protocol.TProtocol prot, Span struct) throws org.apache.thrift.TException {
-      TTupleProtocol oprot = (TTupleProtocol) prot;
-      BitSet optionals = new BitSet();
-      if (struct.isSetTrace_id()) {
-        optionals.set(0);
-      }
-      if (struct.isSetName()) {
-        optionals.set(1);
-      }
-      if (struct.isSetId()) {
-        optionals.set(2);
-      }
-      if (struct.isSetParent_id()) {
-        optionals.set(3);
-      }
-      if (struct.isSetAnnotations()) {
-        optionals.set(4);
-      }
-      if (struct.isSetBinary_annotations()) {
-        optionals.set(5);
-      }
-      if (struct.isSetDebug()) {
-        optionals.set(6);
-      }
-      oprot.writeBitSet(optionals, 7);
-      if (struct.isSetTrace_id()) {
-        oprot.writeI64(struct.trace_id);
-      }
-      if (struct.isSetName()) {
-        oprot.writeString(struct.name);
-      }
-      if (struct.isSetId()) {
-        oprot.writeI64(struct.id);
-      }
-      if (struct.isSetParent_id()) {
-        oprot.writeI64(struct.parent_id);
-      }
-      if (struct.isSetAnnotations()) {
-        {
-          oprot.writeI32(struct.annotations.size());
-          for (Annotation _iter8 : struct.annotations)
-          {
-            _iter8.write(oprot);
-          }
-        }
-      }
-      if (struct.isSetBinary_annotations()) {
-        {
-          oprot.writeI32(struct.binary_annotations.size());
-          for (BinaryAnnotation _iter9 : struct.binary_annotations)
-          {
-            _iter9.write(oprot);
-          }
-        }
-      }
-      if (struct.isSetDebug()) {
-        oprot.writeBool(struct.debug);
-      }
-    }
-
-    @Override
-    public void read(org.apache.thrift.protocol.TProtocol prot, Span struct) throws org.apache.thrift.TException {
-      TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(7);
-      if (incoming.get(0)) {
-        struct.trace_id = iprot.readI64();
-        struct.setTrace_idIsSet(true);
-      }
-      if (incoming.get(1)) {
-        struct.name = iprot.readString();
-        struct.setNameIsSet(true);
-      }
-      if (incoming.get(2)) {
-        struct.id = iprot.readI64();
-        struct.setIdIsSet(true);
-      }
-      if (incoming.get(3)) {
-        struct.parent_id = iprot.readI64();
-        struct.setParent_idIsSet(true);
-      }
-      if (incoming.get(4)) {
-        {
-          org.apache.thrift.protocol.TList _list10 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
-          struct.annotations = new ArrayList<Annotation>(_list10.size);
-          for (int _i11 = 0; _i11 < _list10.size; ++_i11)
-          {
-            Annotation _elem12; // required
-            _elem12 = new Annotation();
-            _elem12.read(iprot);
-            struct.annotations.add(_elem12);
-          }
-        }
-        struct.setAnnotationsIsSet(true);
-      }
-      if (incoming.get(5)) {
-        {
-          org.apache.thrift.protocol.TList _list13 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
-          struct.binary_annotations = new ArrayList<BinaryAnnotation>(_list13.size);
-          for (int _i14 = 0; _i14 < _list13.size; ++_i14)
-          {
-            BinaryAnnotation _elem15; // required
-            _elem15 = new BinaryAnnotation();
-            _elem15.read(iprot);
-            struct.binary_annotations.add(_elem15);
-          }
-        }
-        struct.setBinary_annotationsIsSet(true);
-      }
-      if (incoming.get(6)) {
-        struct.debug = iprot.readBool();
-        struct.setDebugIsSet(true);
-      }
-    }
-  }
-
-}
-
diff --git a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/zipkinCoreConstants.java b/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/zipkinCoreConstants.java
deleted file mode 100644
index f3e1cde..0000000
--- a/htrace-zipkin/src/main/java/com/twitter/zipkin/gen/zipkinCoreConstants.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * Autogenerated by Thrift Compiler (0.9.0)
- *
- * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
- *  @generated
- */
-package com.twitter.zipkin.gen;
-
-import org.apache.thrift.scheme.IScheme;
-import org.apache.thrift.scheme.SchemeFactory;
-import org.apache.thrift.scheme.StandardScheme;
-
-import org.apache.thrift.scheme.TupleScheme;
-import org.apache.thrift.protocol.TTupleProtocol;
-import org.apache.thrift.protocol.TProtocolException;
-import org.apache.thrift.EncodingUtils;
-import org.apache.thrift.TException;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.EnumMap;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.EnumSet;
-import java.util.Collections;
-import java.util.BitSet;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class zipkinCoreConstants {
-
-  public static final String CLIENT_SEND = "cs";
-
-  public static final String CLIENT_RECV = "cr";
-
-  public static final String SERVER_SEND = "ss";
-
-  public static final String SERVER_RECV = "sr";
-
-}
diff --git a/htrace-zipkin/src/main/java/org/htrace/impl/ZipkinSpanReceiver.java b/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
similarity index 96%
rename from htrace-zipkin/src/main/java/org/htrace/impl/ZipkinSpanReceiver.java
rename to htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
index 198b654..fdb4da7 100644
--- a/htrace-zipkin/src/main/java/org/htrace/impl/ZipkinSpanReceiver.java
+++ b/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
@@ -15,14 +15,19 @@
  * limitations under the License.
  */
 
-package org.htrace.impl;
+package org.apache.htrace.impl;
 
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.twitter.zipkin.gen.LogEntry;
 import com.twitter.zipkin.gen.Scribe;
+
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.htrace.HTraceConfiguration;
+import org.apache.htrace.Span;
+import org.apache.htrace.SpanReceiver;
+import org.apache.htrace.zipkin.HTraceToZipkinConverter;
 import org.apache.thrift.protocol.TBinaryProtocol;
 import org.apache.thrift.protocol.TProtocol;
 import org.apache.thrift.protocol.TProtocolFactory;
@@ -31,10 +36,6 @@ import org.apache.thrift.transport.TIOStreamTransport;
 import org.apache.thrift.transport.TSocket;
 import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
-import org.htrace.HTraceConfiguration;
-import org.htrace.Span;
-import org.htrace.SpanReceiver;
-import org.htrace.zipkin.HTraceToZipkinConverter;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -129,17 +130,17 @@ public class ZipkinSpanReceiver implements SpanReceiver {
   private String collectorHostname;
   private int collectorPort;
 
-  public ZipkinSpanReceiver() {
+  public ZipkinSpanReceiver(HTraceConfiguration conf) {
     this.queue = new ArrayBlockingQueue<Span>(1000);
     this.protocolFactory = new TBinaryProtocol.Factory();
 
     tf = new ThreadFactoryBuilder().setDaemon(true)
         .setNameFormat("zipkinSpanReceiver-%d")
         .build();
+    configure(conf);
   }
 
-  @Override
-  public void configure(HTraceConfiguration conf) {
+  private void configure(HTraceConfiguration conf) {
     this.conf = conf;
 
     this.collectorHostname = conf.get("zipkin.collector-hostname",
@@ -273,7 +274,7 @@ public class ZipkinSpanReceiver implements SpanReceiver {
           errorCount = 0;
         } catch (Exception e) {
           LOG.error("Error when writing to the zipkin collector: " +
-              collectorHostname + ":" + collectorPort);
+              collectorHostname + ":" + collectorPort, e);
 
           errorCount += 1;
           // If there have been ten errors in a row start dropping things.
diff --git a/htrace-zipkin/src/main/java/org/htrace/zipkin/HTraceToZipkinConverter.java b/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java
similarity index 95%
rename from htrace-zipkin/src/main/java/org/htrace/zipkin/HTraceToZipkinConverter.java
rename to htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java
index 0a3a60a..09ab1ea 100644
--- a/htrace-zipkin/src/main/java/org/htrace/zipkin/HTraceToZipkinConverter.java
+++ b/htrace-zipkin/src/main/java/org/apache/htrace/zipkin/HTraceToZipkinConverter.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.htrace.zipkin;
+package org.apache.htrace.zipkin;
 
 import com.twitter.zipkin.gen.Annotation;
 import com.twitter.zipkin.gen.AnnotationType;
@@ -22,7 +22,8 @@ import com.twitter.zipkin.gen.BinaryAnnotation;
 import com.twitter.zipkin.gen.Endpoint;
 import com.twitter.zipkin.gen.Span;
 import com.twitter.zipkin.gen.zipkinCoreConstants;
-import org.htrace.TimelineAnnotation;
+
+import org.apache.htrace.TimelineAnnotation;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -99,14 +100,14 @@ public class HTraceToZipkinConverter {
    * <li>Set the last annotation. [SS, CR]
    * </ul>
    */
-  public Span convert(org.htrace.Span hTraceSpan) {
+  public Span convert(org.apache.htrace.Span hTraceSpan) {
     Span zipkinSpan = new Span();
     String serviceName = hTraceSpan.getProcessId().toLowerCase();
     Endpoint ep = new Endpoint(ipv4Address, (short) getPort(serviceName), serviceName);
     List<Annotation> annotationList = createZipkinAnnotations(hTraceSpan, ep);
     List<BinaryAnnotation> binaryAnnotationList = createZipkinBinaryAnnotations(hTraceSpan, ep);
     zipkinSpan.setTrace_id(hTraceSpan.getTraceId());
-    if (hTraceSpan.getParentId() != org.htrace.Span.ROOT_SPAN_ID) {
+    if (hTraceSpan.getParentId() != org.apache.htrace.Span.ROOT_SPAN_ID) {
       zipkinSpan.setParent_id(hTraceSpan.getParentId());
     }
     zipkinSpan.setId(hTraceSpan.getSpanId());
@@ -119,7 +120,7 @@ public class HTraceToZipkinConverter {
   /**
    * Add annotations from the htrace Span.
    */
-  private List<Annotation> createZipkinAnnotations(org.htrace.Span hTraceSpan,
+  private List<Annotation> createZipkinAnnotations(org.apache.htrace.Span hTraceSpan,
                                                    Endpoint ep) {
     List<Annotation> annotationList = new ArrayList<Annotation>();
 
@@ -141,7 +142,7 @@ public class HTraceToZipkinConverter {
    *
    * @return list of Annotations that could be added to Zipkin Span.
    */
-  private List<BinaryAnnotation> createZipkinBinaryAnnotations(org.htrace.Span span,
+  private List<BinaryAnnotation> createZipkinBinaryAnnotations(org.apache.htrace.Span span,
                                                                Endpoint ep) {
     List<BinaryAnnotation> l = new ArrayList<BinaryAnnotation>();
     for (Map.Entry<byte[], byte[]> e : span.getKVAnnotations().entrySet()) {
diff --git a/htrace-zipkin/src/test/java/org/htrace/TestHTraceSpanToZipkinSpan.java b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
similarity index 94%
rename from htrace-zipkin/src/test/java/org/htrace/TestHTraceSpanToZipkinSpan.java
rename to htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
index 80cc2bc..07a9cea 100644
--- a/htrace-zipkin/src/test/java/org/htrace/TestHTraceSpanToZipkinSpan.java
+++ b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
@@ -15,12 +15,15 @@
  * limitations under the License.
  */
 
-package org.htrace;
+package org.apache.htrace;
 
 import com.twitter.zipkin.gen.zipkinCoreConstants;
-import org.htrace.impl.MilliSpan;
-import org.htrace.zipkin.HTraceToZipkinConverter;
-import org.htrace.impl.POJOSpanReceiver;
+
+import org.apache.htrace.Span;
+import org.apache.htrace.Trace;
+import org.apache.htrace.impl.MilliSpan;
+import org.apache.htrace.impl.POJOSpanReceiver;
+import org.apache.htrace.zipkin.HTraceToZipkinConverter;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -39,7 +42,7 @@ public class TestHTraceSpanToZipkinSpan {
 
   @Test
   public void testHTraceToZipkin() throws IOException {
-    POJOSpanReceiver psr = new POJOSpanReceiver();
+    POJOSpanReceiver psr = new POJOSpanReceiver(HTraceConfiguration.EMPTY);
     Trace.addReceiver(psr);
 
     Span rootSpan = new MilliSpan(ROOT_SPAN_DESC, 1, Span.ROOT_SPAN_ID, 100, "test");
diff --git a/pom.xml b/pom.xml
index 7bd29a4..7ce1233 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,26 +11,71 @@ OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache</groupId>
+    <artifactId>apache</artifactId>
+    <version>12</version>
+    <relativePath/>
+    <!-- no parent resolution -->
+  </parent>
 
-  <groupId>org.htrace</groupId>
+  <groupId>org.apache.htrace</groupId>
   <artifactId>htrace</artifactId>
-  <version>3.0.4</version>
+  <version>3.1.0-incubating</version>
   <packaging>pom</packaging>
-
-  <name>htrace</name>
-  <description>Tracing library</description>
+  <name>Apache HTrace</name>
+  <description>A tracing framework for use with distributed systems written in java</description>
+  <url>http://htrace.incubator.apache.org</url>
 
   <modules>
     <module>htrace-core</module>
     <module>htrace-zipkin</module>
+    <module>htrace-hbase</module>
+    <module>htrace-flume</module>
   </modules>
 
-  <parent>
-    <groupId>org.sonatype.oss</groupId>
-    <artifactId>oss-parent</artifactId>
-    <version>7</version>
-  </parent>
-
+  <licenses>
+    <license>
+      <name>The Apache Software License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <distribution>repo</distribution>
+      <comments>A business-friendly OSS license</comments>
+    </license>
+  </licenses>
+  <scm>
+    <connection>scm:git:http://git-wip-us.apache.org/repos/asf/incubator-htrace.git</connection>
+    <developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/incubator-htrace.git</developerConnection>
+    <url>https://git-wip-us.apache.org/repos/asf?p=incubator-htrace.git</url>
+  </scm>
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://issues.apache.org/jira/browse/HTRACE</url>
+  </issueManagement>
+  <ciManagement>
+    <system>hudson</system>
+    <url>https://builds.apache.org/view/H-L/view/HTrace/</url>
+  </ciManagement>
+  <mailingLists>
+    <mailingList>
+      <name>Developer List</name>
+      <subscribe>dev-subscribe at htrace.incubator.apache.org</subscribe>
+      <unsubscribe>dev-unsubscribe at htrace.incubator.apache.org</unsubscribe>
+      <post>dev at htrace.incubator.apache.org</post>
+      <archive>http://mail-archives.apache.org/mod_mbox/incubator-htrace-dev/</archive>
+    </mailingList>
+    <mailingList>
+      <name>Commits List</name>
+      <subscribe>commits-subscribe at htrace.incubator.apache.org</subscribe>
+      <unsubscribe>commits-unsubscribe at htrace.incubator.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/incubator-htrace-commits/</archive>
+    </mailingList>
+    <mailingList>
+      <name>Issues List</name>
+      <subscribe>issues-subscribe at htrace.incubator.apache.org</subscribe>
+      <unsubscribe>issues-unsubscribe at htrace.incubator.apache.org</unsubscribe>
+      <archive>http://mail-archives.apache.org/mod_mbox/incubator-htrace-issues/</archive>
+    </mailingList>
+  </mailingLists>
   <developers>
     <developer>
       <id>jon</id>
@@ -49,21 +94,6 @@ language governing permissions and limitations under the License. -->
       <organizationUrl>http://www.cloudera.com</organizationUrl>
     </developer>
   </developers>
-
-  <url>https://github.com/cloudera/htrace</url>
-  <licenses>
-    <license>
-      <name>The Apache Software License, Version 2.0</name>
-      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
-      <distribution>repo</distribution>
-      <comments>A business-friendly OSS license</comments>
-    </license>
-  </licenses>
-  <scm>
-    <connection>scm:git:git at github.com:cloudera/htrace.git</connection>
-    <developerConnection>scm:git:git at github.com:cloudera/htrace.git</developerConnection>
-    <url>scm:git:git at github.com:cloudera/htrace.git</url>
-  </scm>
   <build>
     <pluginManagement>
       <plugins>
@@ -95,9 +125,21 @@ language governing permissions and limitations under the License. -->
           </executions>
         </plugin>
         <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jar-plugin</artifactId>
+          <version>2.4</version>
+          <executions>
+            <execution>
+              <goals>
+                <goal>test-jar</goal>
+              </goals>
+            </execution>
+          </executions>
+        </plugin>
+        <plugin>
           <groupId>org.apache.rat</groupId>
           <artifactId>apache-rat-plugin</artifactId>
-          <version>0.10</version>
+          <version>0.11</version>
           <executions>
             <execution>
               <phase>package</phase>
@@ -108,12 +150,24 @@ language governing permissions and limitations under the License. -->
           </executions>
           <configuration>
             <excludes>
-              <!--  exclude source control files -->
-              <exclude>.git/**</exclude>
-              <exclude>.svn/**</exclude>
-              <exclude>**/.settings/**</exclude>
-              <!-- Readme -->
-              <exclue>README.md</exclue>
+                <exclude>**/.settings/**</exclude>
+                <exclude>**/dependency-reduced-pom.xml</exclude>
+                <exclude>**/generated/**</exclude>
+                <exclude>*/.settings/*</exclude>
+                <exclude>*/generated/*</exclude>
+                <exclude>.git/**</exclude>
+                <exclue>**/README.md</exclue>
+                <exclude>**/src/go/bin/*</exclude>
+                <exclude>**/src/go/pkg/*</exclude>
+                <!-- external projects -->
+                <exclude>**/bootstrap-3.3.1/**</exclude>
+                <exclude>**/*.min.js</exclude>
+                <exclude>**/d3.min.js</exclude>
+                <!-- Pulled down sources -->
+                <exclude>**/gopkg.in/**</exclude>
+                <exclude>**/github.com/**</exclude>
+                <exclude>**/golang.org/**</exclude>
+                <exclude>**/Godeps/**</exclude>
             </excludes>
           </configuration>
         </plugin>
@@ -160,6 +214,11 @@ language governing permissions and limitations under the License. -->
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.1</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-gpg-plugin</artifactId>
       </plugin>
       <plugin>
@@ -167,13 +226,64 @@ language governing permissions and limitations under the License. -->
         <artifactId>maven-deploy-plugin</artifactId>
       </plugin>
       <plugin>
-        <groupId>org.apache.rat</groupId>
-        <artifactId>apache-rat-plugin</artifactId>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <version>3.4</version>
+        <dependencies>
+          <dependency>
+            <!-- add support for ssh/scp -->
+            <groupId>org.apache.maven.wagon</groupId>
+            <artifactId>wagon-ssh</artifactId>
+            <version>2.2</version>
+          </dependency>
+          <dependency>
+            <groupId>org.apache.maven.doxia</groupId>
+            <artifactId>doxia-module-markdown</artifactId>
+            <version>1.6</version>
+          </dependency>
+        <dependency>
+          <groupId>lt.velykis.maven.skins</groupId>
+          <artifactId>reflow-velocity-tools</artifactId>
+          <version>1.1.1</version>
+        </dependency>
+        <!-- Reflow skin requires Velocity >= 1.7  -->
+        <dependency>
+          <groupId>org.apache.velocity</groupId>
+          <artifactId>velocity</artifactId>
+          <version>1.7</version>
+        </dependency>
+        </dependencies>
+        <configuration>
+          <siteDirectory>${basedir}/src/main/site</siteDirectory>
+          <inputEncoding>UTF-8</inputEncoding>
+          <outputEncoding>UTF-8</outputEncoding>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.5.3</version>
+        <configuration>
+          <!--Do not attach assembly to project.-->
+          <attach>false</attach>
+          <tarLongFileMode>gnu</tarLongFileMode>
+          <finalName>htrace-${project.version}</finalName>
+          <descriptors>
+            <descriptor>src/main/assembly/src.xml</descriptor>
+          </descriptors>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <redirectTestOutputToFile>true</redirectTestOutputToFile>
+        </configuration>
       </plugin>
     </plugins>
   </build>
   <properties>
     <targetJdk>1.6</targetJdk>
+    <failIfNoTests>false</failIfNoTests>
   </properties>
   <dependencyManagement>
     <dependencies>
@@ -195,4 +305,14 @@ language governing permissions and limitations under the License. -->
       </dependency>
     </dependencies>
   </dependencyManagement>
+  <distributionManagement>
+    <site>
+      <id>htrace.incubator.apache.org</id>
+      <name>HTrace Website at incubator.apache.org</name>
+      <!-- On why this is the tmp dir and not hbase.apache.org, see
+               https://issues.apache.org/jira/browse/HBASE-7593?focusedCommentId=13555866&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-13555866
+               -->
+      <url>file:///tmp</url>
+    </site>
+  </distributionManagement>
 </project>
diff --git a/src/main/assembly/src.xml b/src/main/assembly/src.xml
new file mode 100644
index 0000000..3709375
--- /dev/null
+++ b/src/main/assembly/src.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1 http://maven.apache.org/xsd/assembly-1.1.1.xsd">
+<!--
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+  <!--Copies over all you need to build hbase-->
+  <id>src</id>
+  <formats>
+    <format>tar.gz</format>
+  </formats>
+  <moduleSets>
+    <moduleSet>
+      <!-- Enable access to all projects in the current multimodule build. Eclipse
+        says this is an error, but builds from the command line just fine. -->
+      <useAllReactorProjects>true</useAllReactorProjects>
+      <!-- Include all the sources in the top directory -->
+      <sources>
+        <fileSets>
+          <fileSet>
+            <excludes>
+              <exclude>target/</exclude>
+              <exclude>test/</exclude>
+              <exclude>.classpath</exclude>
+              <exclude>**/*.orig</exclude>
+              <exclude>.project</exclude>
+              <exclude>.git</exclude>
+              <exclude>.gitignore</exclude>
+              <exclude>.settings/</exclude>
+              <exclude>**/src/go/bin/**</exclude>
+              <exclude>**/pkg/**</exclude>
+              <exclude>**/build/**</exclude>
+            </excludes>
+          </fileSet>
+        </fileSets>
+      </sources>
+    </moduleSet>
+  </moduleSets>
+  <fileSets>
+    <fileSet>
+      <directory>${project.basedir}/src</directory>
+      <outputDirectory>src</outputDirectory>
+      <fileMode>0644</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <!-- Include top level bin directory -->
+    <fileSet>
+        <directory>${project.basedir}/bin</directory>
+      <outputDirectory>bin</outputDirectory>
+      <fileMode>0755</fileMode>
+      <directoryMode>0755</directoryMode>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/src</directory>
+      <outputDirectory>src</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/tools</directory>
+      <outputDirectory>tools</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}</directory>
+      <outputDirectory>.</outputDirectory>
+      <includes>
+        <include>pom.xml</include>
+        <include>LICENSE.txt</include>
+        <include>NOTICE.txt</include>
+        <include>CHANGES.txt</include>
+        <include>DISCLAIMER.txt</include>
+        <include>BUILDING.txt</include>
+        <include>README.md</include>
+      </includes>
+      <fileMode>0644</fileMode>
+    </fileSet>
+</fileSets>
+</assembly>
diff --git a/README.md b/src/main/site/markdown/index.md
similarity index 63%
copy from README.md
copy to src/main/site/markdown/index.md
index 911a6ab..1e3f866 100644
--- a/README.md
+++ b/src/main/site/markdown/index.md
@@ -1,47 +1,60 @@
-HTrace
-======
-HTrace is a tracing framework intended for use with distributed systems written in java.  
+<!---
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
 
-The project is hosted at http://github.com/cloudera/htrace.  
-The project is available in Maven Central with groupId: org.htrace, and name: htrace.  
-(It was formally at groupId: org.cloudera.htrace, and name: htrace).  
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License. See accompanying LICENSE file.
+-->
+
+Apache HTrace is an <a href="http://htrace.incubator.apache.org">Apache Incubator</a>
+project. To add HTrace to your project, see detail on how to add it as a
+<a href="dependency-info.html">dependency</a>.
+
+Formerly, HTrace was available at org.htrace.
 
 API
 ---
-Using HTrace requires some instrumentation to your application.  
-Before we get into that we have to review our terminology.  HTrace
+Using HTrace requires adding some instrumentation to your application.
+Before we get into the details, lets review our terminology.  HTrace
 borrows [Dapper's](http://research.google.com/pubs/pub36356.html)
-terminology.  
-  
+terminology.
+
 <b>Span:</b> The basic unit of work. For example, sending an RPC is a
-new span, as is sending a response to an RPC.  
+new span, as is sending a response to an RPC.
 Span's are identified by a unique 64-bit ID for the span and another
 64-bit ID for the trace the span is a part of.  Spans also have other
 data, such as descriptions, key-value annotations, the ID of the span
-that caused them, and process ID's (normally IP address).  
-<br>
+that caused them, and process ID's (normally IP address).
+
 Spans are started and stopped, and they keep track of their timing
 information.  Once you create a span, you must stop it at some point
-in the future.  
-  
+in the future.
+
 <b>Trace:</b> A set of spans forming a tree-like structure.  For
 example, if you are running a distributed big-data store, a trace
-might be formed by a put request. 
+might be formed by a put request.
 
-To instrument your system you must:  
-<br>
-<b>1. Attach additional information to your RPC's.</b>  
+### How to add tracing to your application
+To instrument your system you must:
+
+<b>1. Attach additional information to your RPC's.</b>
 In order to create the causal links necessary for a trace, HTrace
 needs to know about the causal
 relationships between spans.  The only information you need to add to
 your RPC's is two 64-bit longs.  If tracing is enabled (Trace.isTracing()
 returns true) when you send an RPC, attach the ID of the current span
-and the ID of the current trace to the message.  
+and the ID of the current trace to the message.
 On the receiving end of the RPC, check to see if the message has the
 additional tracing information above.  If it does, start a new span
-with the information given (more on that in a bit).  
-<br>
-<b>2. Wrap your thread changes.</b>  
+with the information given (more on that in a bit).
+
+<b>2. Wrap your thread changes.</b>
 HTrace stores span information in java's ThreadLocals, which causes
 the trace to be "lost" on thread changes. The only way to prevent
 this is to "wrap" your thread changes. For example, if your code looks
@@ -49,10 +62,10 @@ like this:
 
 ````java
     Thread t1 = new Thread(new MyRunnable());
-    ...  
+    ...
 ````
 
-Just change it to look this:  
+Just change it to look this:
 
 ````java
     Thread t1 = new Thread(Trace.wrap(new MyRunnable()));
@@ -67,37 +80,37 @@ created the runnable/callable.  There may be situations in which a
 simple `Trace.wrap()` does not suffice.  In these cases all you need
 to do is keep a reference to the "parent span" (the span before the
 thread change) and once you're in the new thread start a new span that
-is the "child" of the parent span you stored.  
-<br>
-For example:  
-<br>
+is the "child" of the parent span you stored.
+
+For example:
+
 Say you have some object representing a "put" operation.  When the
 client does a "put," the put is first added to a list so another
 thread can batch together the puts. In this situation, you
 might want to add another field to the Put class that could store the
 current span at the time the put was created.  Then when the put is
 pulled out of the list to be processed, you can start a new span as
-the child of the span stored in the Put.  
-<br>
-<b>3. Add custom spans and annotations.</b>  
+the child of the span stored in the Put.
+
+<b>3. Add custom spans and annotations.</b>
 Once you've augmented your RPC's and wrapped the necessary thread
-changes, you can add more spans and annotations wherever you want.  
+changes, you can add more spans and annotations wherever you want.
 For example, you might do some expensive computation that you want to
 see on your traces.  In this case, you could start a new span before
 the computation that you then stop after the computation has
-finished. It might look like this:  
+finished. It might look like this:
 
 ````java
-    Span computationSpan = Trace.startSpan("Expensive computation.");  
-    try {  
-        //expensive computation here  
-    } finally {  
-        computationSpan.stop();  
-    }  
+    Span computationSpan = Trace.startSpan("Expensive computation.");
+    try {
+        //expensive computation here
+    } finally {
+        computationSpan.stop();
+    }
 ````
 
-HTrace also supports key-value annotations on a per-trace basis.  
-<br>
+HTrace also supports key-value annotations on a per-trace basis.
+
 Example:
 
 ````java
@@ -109,14 +122,14 @@ not tracing, but instead it will return a `NullSpan`, which does
 nothing on any of its method calls. The takeaway here is you can call
 methods on the `currentTrace()` without fear of NullPointerExceptions.
 
-###Samplers  
-`Sampler` is an interface that defines one function:  
+###Samplers
+`Sampler` is an interface that defines one function:
 
 ````java
     boolean next(T info);
 ````
 
-All of the `Trace.startSpan()` methods can take an optional sampler.  
+All of the `Trace.startSpan()` methods can take an optional sampler.
 A new span is only created if the sampler's next function returns
 true.  If the Sampler returns false, the `NullSpan` is returned from
 `startSpan()`, so it's safe to call `stop()` or `addAnnotation()` on it.
@@ -124,33 +137,43 @@ As you may have noticed from the `next()` method signature, Sampler is
 parameterized.  The argument to `next()` is whatever piece of
 information you might need for sampling.  See `Sampler.java` for an
 example of this.  If you do not require any additional information,
-then just ignore the parameter.  
+then just ignore the parameter.
 HTrace includes  a sampler that always returns true, a
 sampler that always returns false and a sampler returns true some
-percentage of the time (you pass in the percentage as a decimal at construction). 
+percentage of the time (you pass in the percentage as a decimal at construction).
+
+HTrace comes with several standard samplers, including `AlwaysSampler`,
+`NeverSampler`, `ProbabilitySampler`, and `CountSampler`.  An application can
+use the `SamplerBuilder` to create one of these standard samplers based on the
+current configuration.
+
+````java
+  HTraceConfiguration hconf = createMyHTraceConfiguration();
+  Sampler sampler = new SamplerBuilder(hconf).build();
+````
 
-###`Trace.startSpan()` 
-There is a single method to create and start spans: `startSpan()`.  
+####Trace.startSpan()
+There is a single method to create and start spans: `startSpan()`.
 For the `startSpan()` methods that do not take an explicit Sampler, the
 default Sampler is used.  The default sampler returns true if and only
 if tracing is already on in the current thread.  That means that
 calling `startSpan()` with no explicit Sampler is a good idea when you
 have information that you would like to add to a trace if it's already
 occurring, but is not something you would want to start a whole new
-trace for.  
-<br>
+trace for.
+
 If you are using a sampler that makes use of the `T info` parameter to
 `next()`, just pass in the object as the last argument.  If you leave it
 out, HTrace will pass `null` for you (so make sure your Samplers can
-handle `null`).  
-<br>
+handle `null`).
+
 Aside from whether or not you pass in an explicit `Sampler`, there are
-other options you have when calling `startSpan()`.  
+other options you have when calling `startSpan()`.
 For the next section I am assuming you are familiar with the options
 for passing in `Samplers` and `info` parameters, so when I say "no
 arguments," I mean no additional arguments other than whatever
-`Sampler`/`info` parameters you deem necessary.  
-<br>
+`Sampler`/`info` parameters you deem necessary.
+
 You can call `startSpan()` with no additional arguments.
 In this case, `Trace.java` will start a span if the sampler (explicit
 or default) returns true. If the current span is not the `NullSpan`, the span
@@ -164,47 +187,64 @@ receiver of the RPC will check the message for the additional two
 `longs` and will call `startSpan()` if they are attached.  The last
 `startSpan()` takes a `Span parent`.  The result of `parent.child()`
 will be used for the new span.  `Span.child()` simply returns a span
-that is a child of `this`. 
+that is a child of `this`.
 
 ###Span Receivers
 In order to use the tracing information consisting of spans,
-you need implementation of `SpanReceiver` interface which collects spans
+you need an implementation of `SpanReceiver` interface which collects spans
 and typically writes it to files or databases or collector services.
-The `SpanReceiver` implementation must provide `receiveSpan` method which
+The `SpanReceiver` implementation must provide a `receiveSpan` method which
 is called from `Trace.deliver` method.
 You do not need to explicitly call `Trace.deliver`
 because it is internally called by the implementation of `Span`.
 
 ````java
     public interface SpanReceiver extends Closeable {
-      public void configure(HTraceConfiguration conf);
       public void receiveSpan(Span span);
     }
 ````
 
-Each application process using HTrace needs to
-initialize the SpanReceiver implementation and register it first
-by calling `Trace.addReceiver` method.
+HTrace comes with several standard span receivers, such as
+`LocalFileSpanReceiver`.  An application can use the `SpanReceiverBuilder` to
+create a particular type of standard `SpanReceiver` based on the current
+configuration.  Once a SpanReceiver has been created, it should be registered
+with the HTrace framework by calling `Trace.addReceiver`.
 
 ````java
-    // load and instanciate SpanReceiver impl.
-    // ...
-    impl.configure(conf);
-    Trace.addReceiver(impl);
+    HTraceConfiguration hconf = createMyHTraceConfiguration();
+    SpanReceiverBuilder builder = new SpanReceiverBuilder(hconf);
+    SpanReceiver spanReceiver = builder.build();
+    if (spanReceiver != null) {
+      Trace.addReceiver(spanReceiver);
+    }
 ````
 
+####Zipkin
 htrace-zipkin provides the `SpanReceiver` implementation
 which sends spans to [Zipkin](https://github.com/twitter/zipkin) collector.
-You can build the uber-jar (htrace-zipkin-*-jar-withdependency.jar) for manual setup as shown below.
-This ubse-jar contains depencdencies except htrace-core and its dependencies.
+You can build the uber-jar (htrace-zipkin-*-jar-withdependency.jar) for manual
+setup as shown below.  This uber-jar contains all dependencies except
+htrace-core and its dependencies.
 
     $ cd htrace-zipkin
     $ mvn compile assembly:single
 
+####HBase Receiver
+See htrace-hbase for an Span Receiver implementation that writes HBase.
+Also bundled is a simple Span Viewer.
+
 Testing Information
 -------------------------------
 
-The test that creates a sample trace (TestHTrace) takes a command line argument telling it where to write span information. Run mvn test -DargLine="-DspanFile=FILE\_PATH" to write span information to FILE_PATH. If no file is specified, span information will be written to standard out. If span information is written to a file, you can use the included graphDrawer python script in tools/ to create a simple visualization of the trace. Or you could write some javascript to make a better visu [...]
+The test that creates a sample trace (TestHTrace) takes a command line
+argument telling it where to write span information. Run
+`mvn test -DargLine="-DspanFile=FILE\_PATH"` to write span
+information to FILE_PATH. If no file is specified, span information
+will be written to standard out. If span information is written to a
+file, you can use the included graphDrawer python script in tools/
+to create a simple visualization of the trace. Or you could write
+some javascript to make a better visualization, and send a pull
+request if you do :).
 
 Publishing to Maven Central
 -------------------------------
diff --git a/src/main/site/site.xml b/src/main/site/site.xml
new file mode 100644
index 0000000..0c22c17
--- /dev/null
+++ b/src/main/site/site.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd">
+  <bannerLeft>
+    <name>Apache HTrace</name>
+    <href>http://htrace.incubator.apache.org/</href>
+  </bannerLeft>
+  <bannerRight>
+    <name />
+  </bannerRight>
+  <body>
+    <menu name="Apache HTrace Project">
+      <item name="Overview" href="index.html"/>
+      <item name="License" href="license.html" />
+      <item name="Downloads" href="http://www.apache.org/dyn/closer.cgi/htrace/" />
+      <item name="Distribution" href="dependency-management.html" />
+      <item name="Dependency" href="dependency-info.html" />
+      <item name="Issue Tracking" href="issue-tracking.html" />
+      <item name="CI" href="integration.html" />
+      <item name="Mailing Lists" href="mail-lists.html" />
+      <item name="Source Repository" href="source-repository.html" />
+      <item name="Modules" href="modules.html" />
+      <item name="Project Info" href="project-info.html" />
+      <item name="Team" href="team-list.html" />
+    </menu>
+    <menu name="ASF">
+        <item name="Apache Software Foundation"      href="http://www.apache.org/foundation/" />
+        <item name="How Apache Works"      href="http://www.apache.org/foundation/how-it-works.html" />
+        <item name="Sponsoring Apache"      href="http://www.apache.org/foundation/sponsorship.html" />
+    </menu>
+  </body>
+ <custom>
+    <reflowSkin>
+      <theme>bootswatch-flatly</theme>
+      <toc>top</toc>
+      <highlightJs>true</highlightJs>
+      <slogan position="bannerRight">A tracing framework for use with distributed systems written in java</slogan>
+      <bottomNav>
+        <column>Home</column>
+      </bottomNav>
+    </reflowSkin>
+ </custom>
+    <skin>
+      <groupId>lt.velykis.maven.skins</groupId>
+      <artifactId>reflow-maven-skin</artifactId>
+      <version>1.1.1</version>
+    </skin>
+</project>

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/htrace.git



More information about the pkg-java-commits mailing list