You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@htrace.apache.org by cm...@apache.org on 2015/09/20 06:12:05 UTC

[1/5] incubator-htrace git commit: HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)

Repository: incubator-htrace
Updated Branches:
  refs/heads/master d8409ef9a -> 3c20489f8


http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.java
new file mode 100644
index 0000000..1e842c5
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestTracerId.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.core;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class TestTracerId {
+  private void testTracerIdImpl(String expected, String fmt) {
+    assertEquals(expected, new TracerId(
+        HTraceConfiguration.fromKeyValuePairs(TracerId.TRACER_ID_KEY, fmt),
+        "TracerName").get());
+  }
+
+  @Test
+  public void testSimpleTracerIds() {
+    testTracerIdImpl("abc", "abc");
+    testTracerIdImpl("abc", "a\\bc");
+    testTracerIdImpl("abc", "ab\\c");
+    testTracerIdImpl("abc", "\\a\\b\\c");
+    testTracerIdImpl("a\\bc", "a\\\\bc");
+  }
+
+  @Test
+  public void testSubstitutionVariables() throws IOException {
+    testTracerIdImpl("myTracerName", "my%{tname}");
+    testTracerIdImpl(TracerId.getProcessName(), "%{pname}");
+    testTracerIdImpl("my." + TracerId.getProcessName(), "my.%{pname}");
+    testTracerIdImpl(TracerId.getBestIpString() + ".str", "%{ip}.str");
+    testTracerIdImpl("%{pname}", "\\%{pname}");
+    testTracerIdImpl("%cash%money{}", "%cash%money{}");
+    testTracerIdImpl("Foo." + Long.valueOf(TracerId.getOsPid()).toString(),
+        "Foo.%{pid}");
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java b/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java
new file mode 100644
index 0000000..b843999
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TraceCreator.java
@@ -0,0 +1,141 @@
+/*
+ * 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.core;
+
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Does some stuff and traces it.
+ */
+public class TraceCreator {
+  public static final String RPC_TRACE_ROOT = "createSampleRpcTrace";
+  public static final String THREADED_TRACE_ROOT = "createThreadedTrace";
+  public static final String SIMPLE_TRACE_ROOT = "createSimpleTrace";
+
+  private final Tracer tracer;
+
+  public TraceCreator(Tracer tracer) {
+    this.tracer = tracer;
+  }
+
+  public void createSampleRpcTrace() {
+    TraceScope s = tracer.newScope(RPC_TRACE_ROOT);
+    try {
+      pretendRpcSend();
+    } finally {
+      s.close();
+    }
+  }
+
+  public void createSimpleTrace() {
+    TraceScope s = tracer.newScope(SIMPLE_TRACE_ROOT);
+    try {
+      importantWork1();
+    } finally {
+      s.close();
+    }
+  }
+
+  /**
+   * Creates the demo trace (will create different traces from call to call).
+   */
+  public void createThreadedTrace() {
+    TraceScope s = tracer.newScope(THREADED_TRACE_ROOT);
+    try {
+      Random r = ThreadLocalRandom.current();
+      int numThreads = r.nextInt(4) + 1;
+      Thread[] threads = new Thread[numThreads];
+
+      for (int i = 0; i < numThreads; i++) {
+        threads[i] = new Thread(tracer.wrap(new MyRunnable(), null));
+      }
+      for (int i = 0; i < numThreads; i++) {
+        threads[i].start();
+      }
+      for (int i = 0; i < numThreads; i++) {
+        try {
+          threads[i].join();
+        } catch (InterruptedException e) {
+        }
+      }
+      importantWork1();
+    } finally {
+      s.close();
+    }
+  }
+
+  private void importantWork1() {
+    TraceScope cur = tracer.newScope("important work 1");
+    try {
+      Thread.sleep((long) (2000 * Math.random()));
+      importantWork2();
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+    } finally {
+      cur.close();
+    }
+  }
+
+  private void importantWork2() {
+    TraceScope cur = tracer.newScope("important work 2");
+    try {
+      Thread.sleep((long) (2000 * Math.random()));
+    } catch (InterruptedException e) {
+      Thread.currentThread().interrupt();
+    } finally {
+      cur.close();
+    }
+  }
+
+  private class MyRunnable implements Runnable {
+    @Override
+    public void run() {
+      try {
+        Thread.sleep(750);
+        Random r = ThreadLocalRandom.current();
+        int importantNumber = 100 / r.nextInt(3);
+        System.out.println("Important number: " + importantNumber);
+      } catch (InterruptedException ie) {
+        Thread.currentThread().interrupt();
+      } catch (ArithmeticException ae) {
+        TraceScope c = tracer.newScope("dealing with arithmetic exception.");
+        try {
+          Thread.sleep((long) (3000 * Math.random()));
+        } catch (InterruptedException ie1) {
+          Thread.currentThread().interrupt();
+        } finally {
+          c.close();
+        }
+      }
+    }
+  }
+
+  public void pretendRpcSend() {
+    Span span = tracer.getCurrentSpan();
+    pretendRpcReceiveWithTraceInfo(span.getSpanId());
+  }
+
+  public void pretendRpcReceiveWithTraceInfo(SpanId parentId) {
+    TraceScope s = tracer.newScope("received RPC", parentId);
+    try {
+      importantWork1();
+    } finally {
+      s.close();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java b/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.java
new file mode 100644
index 0000000..a06e620
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TraceGraph.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.core;
+
+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.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * Used to create the graph formed by spans.
+ */
+public class TraceGraph {
+  private static final Log LOG = LogFactory.getLog(Tracer.class);
+
+
+  public static class SpansByParent {
+    /**
+     * Compare two spans by span ID.
+     */
+    private static Comparator<Span> COMPARATOR =
+        new Comparator<Span>() {
+          @Override
+          public int compare(Span a, Span b) {
+            return a.getSpanId().compareTo(b.getSpanId());
+          }
+        };
+
+    private final TreeSet<Span> treeSet;
+
+    private final HashMap<SpanId, LinkedList<Span>> parentToSpans;
+
+    SpansByParent(Collection<Span> spans) {
+      TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR);
+      parentToSpans = new HashMap<SpanId, LinkedList<Span>>();
+      for (Span span : spans) {
+        treeSet.add(span);
+        for (SpanId parent : span.getParents()) {
+          LinkedList<Span> list = parentToSpans.get(parent);
+          if (list == null) {
+            list = new LinkedList<Span>();
+            parentToSpans.put(parent, list);
+          }
+          list.add(span);
+        }
+        if (span.getParents().length == 0) {
+          LinkedList<Span> list = parentToSpans.get(SpanId.INVALID);
+          if (list == null) {
+            list = new LinkedList<Span>();
+            parentToSpans.put(SpanId.INVALID, list);
+          }
+          list.add(span);
+        }
+      }
+      this.treeSet = treeSet;
+    }
+
+    public List<Span> find(SpanId parentId) {
+      LinkedList<Span> spans = parentToSpans.get(parentId);
+      if (spans == null) {
+        return new LinkedList<Span>();
+      }
+      return spans;
+    }
+
+    public Iterator<Span> iterator() {
+      return Collections.unmodifiableSortedSet(treeSet).iterator();
+    }
+  }
+
+  public static class SpansByTracerId {
+    /**
+     * Compare two spans by process ID, and then by span ID.
+     */
+    private static Comparator<Span> COMPARATOR =
+        new Comparator<Span>() {
+          @Override
+          public int compare(Span a, Span b) {
+            int cmp = a.getTracerId().compareTo(b.getTracerId());
+            if (cmp != 0) {
+              return cmp;
+            }
+            return a.getSpanId().compareTo(b.getSpanId());
+          }
+        };
+
+    private final TreeSet<Span> treeSet;
+
+    SpansByTracerId(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 tracerId) {
+      List<Span> spans = new ArrayList<Span>();
+      Span span = new MilliSpan.Builder().
+                    spanId(SpanId.INVALID).
+                    tracerId(tracerId).
+                    build();
+      while (true) {
+        span = treeSet.higher(span);
+        if (span == null) {
+          break;
+        }
+        if (span.getTracerId().equals(tracerId)) {
+          break;
+        }
+        spans.add(span);
+      }
+      return spans;
+    }
+
+    public Iterator<Span> iterator() {
+      return Collections.unmodifiableSortedSet(treeSet).iterator();
+    }
+  }
+
+  private final SpansByParent spansByParent;
+  private final SpansByTracerId spansByTracerId;
+
+  /**
+   * Create a new TraceGraph
+   *
+   * @param spans The collection of spans to use to create this TraceGraph. Should
+   *              have at least one root span.
+   */
+  public TraceGraph(Collection<Span> spans) {
+    this.spansByParent = new SpansByParent(spans);
+    this.spansByTracerId = new SpansByTracerId(spans);
+  }
+
+  public SpansByParent getSpansByParent() {
+    return spansByParent;
+  }
+
+  public SpansByTracerId getSpansByTracerId() {
+    return spansByTracerId;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder bld = new StringBuilder();
+    String prefix = "";
+    for (Iterator<Span> iter = spansByParent.iterator(); iter.hasNext();) {
+      Span span = iter.next();
+      bld.append(prefix).append(span.toString());
+      prefix = "\n";
+    }
+    return bld.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java b/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java
new file mode 100644
index 0000000..7cb4aed
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/util/TestUtil.java
@@ -0,0 +1,91 @@
+/*
+ * 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.util;
+
+import java.io.File;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Utilities for writing unit tests.
+ */
+public class TestUtil {
+  /**
+   * Get a dump of the stack traces of all threads.
+   */
+  public static String threadDump() {
+    StringBuilder dump = new StringBuilder();
+    Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
+    for (Map.Entry<Thread, StackTraceElement[]> e : stackTraces.entrySet()) {
+      Thread thread = e.getKey();
+      dump.append(String.format(
+          "\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s",
+          thread.getName(),
+          (thread.isDaemon() ? "daemon" : ""),
+          thread.getPriority(),
+          thread.getId(),
+          Thread.State.WAITING.equals(thread.getState()) ?
+              "in Object.wait()" : thread.getState().name().toLowerCase(),
+          Thread.State.WAITING.equals(thread.getState()) ?
+              "WAITING (on object monitor)" : thread.getState()));
+      for (StackTraceElement stackTraceElement : e.getValue()) {
+        dump.append("\n        at ");
+        dump.append(stackTraceElement);
+      }
+      dump.append("\n");
+    }
+    return dump.toString();
+  }
+
+  /**
+   * A callback which returns a value of type T.
+   *
+   * TODO: remove this when we're on Java 8, in favor of
+   * java.util.function.Supplier.
+   */
+  public interface Supplier<T> {
+    T get();
+  }
+
+  /**
+   * Wait for a condition to become true for a configurable amount of time.
+   *
+   * @param check           The condition to wait for.
+   * @param periodMs        How often to check the condition, in milliseconds.
+   * @param timeoutMs       How long to wait in total, in milliseconds.
+   */
+  public static void waitFor(Supplier<Boolean> check, 
+      long periodMs, long timeoutMs)
+          throws TimeoutException, InterruptedException
+  {
+    long endNs = System.nanoTime() +
+        TimeUnit.NANOSECONDS.convert(timeoutMs, TimeUnit.MILLISECONDS);
+    while (true) {
+      boolean result = check.get();
+      if (result) {
+        return;
+      }
+      long nowNs = System.nanoTime();
+      if (nowNs >= endNs) {
+        throw new TimeoutException("Timed out waiting for test condition. " +
+            "Thread dump:\n" + threadDump());
+      }
+      Thread.sleep(periodMs);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 0e0342f..42e19ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@ language governing permissions and limitations under the License. -->
 
   <modules>
     <module>htrace-c</module>
-    <module>htrace-core</module>
+    <module>htrace-core4</module>
     <module>htrace-webapp</module>
     <module>htrace-zipkin</module>
     <module>htrace-hbase</module>


[5/5] incubator-htrace git commit: HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)

Posted by cm...@apache.org.
HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)


Project: http://git-wip-us.apache.org/repos/asf/incubator-htrace/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-htrace/commit/3c20489f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-htrace/tree/3c20489f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-htrace/diff/3c20489f

Branch: refs/heads/master
Commit: 3c20489f82135341e86f1725b748320f373a4afe
Parents: d8409ef
Author: Colin P. Mccabe <cm...@apache.org>
Authored: Sat Sep 19 21:09:06 2015 -0700
Committer: Colin P. Mccabe <cm...@apache.org>
Committed: Sat Sep 19 21:11:59 2015 -0700

----------------------------------------------------------------------
 htrace-core/pom.xml                             | 125 ----
 .../org/apache/htrace/core/AlwaysSampler.java   |  32 -
 .../org/apache/htrace/core/CountSampler.java    |  39 --
 .../apache/htrace/core/HTraceConfiguration.java | 109 ---
 .../htrace/core/LocalFileSpanReceiver.java      | 257 -------
 .../java/org/apache/htrace/core/MilliSpan.java  | 347 ----------
 .../org/apache/htrace/core/NeverSampler.java    |  32 -
 .../java/org/apache/htrace/core/NullScope.java  |  69 --
 .../apache/htrace/core/POJOSpanReceiver.java    |  49 --
 .../apache/htrace/core/ProbabilitySampler.java  |  45 --
 .../java/org/apache/htrace/core/Sampler.java    | 131 ----
 .../main/java/org/apache/htrace/core/Span.java  | 193 ------
 .../java/org/apache/htrace/core/SpanId.java     | 146 ----
 .../org/apache/htrace/core/SpanReceiver.java    | 164 -----
 .../htrace/core/StandardOutSpanReceiver.java    |  42 --
 .../apache/htrace/core/TimelineAnnotation.java  |  40 --
 .../org/apache/htrace/core/TraceCallable.java   |  56 --
 .../htrace/core/TraceExecutorService.java       | 121 ----
 .../org/apache/htrace/core/TraceRunnable.java   |  54 --
 .../java/org/apache/htrace/core/TraceScope.java | 128 ----
 .../java/org/apache/htrace/core/Tracer.java     | 673 -------------------
 .../java/org/apache/htrace/core/TracerId.java   | 294 --------
 .../java/org/apache/htrace/core/TracerPool.java | 285 --------
 .../org/apache/htrace/core/TestBadClient.java   | 205 ------
 .../apache/htrace/core/TestCountSampler.java    |  41 --
 .../java/org/apache/htrace/core/TestHTrace.java | 130 ----
 .../htrace/core/TestHTraceConfiguration.java    |  62 --
 .../htrace/core/TestLocalFileSpanReceiver.java  |  65 --
 .../org/apache/htrace/core/TestMilliSpan.java   | 145 ----
 .../org/apache/htrace/core/TestNullScope.java   |  43 --
 .../org/apache/htrace/core/TestSampler.java     | 100 ---
 .../java/org/apache/htrace/core/TestSpanId.java |  72 --
 .../htrace/core/TestSpanReceiverBuilder.java    | 127 ----
 .../org/apache/htrace/core/TestTracerId.java    |  52 --
 .../org/apache/htrace/core/TraceCreator.java    | 141 ----
 .../java/org/apache/htrace/core/TraceGraph.java | 176 -----
 .../java/org/apache/htrace/util/TestUtil.java   |  91 ---
 htrace-core4/pom.xml                            | 125 ++++
 .../org/apache/htrace/core/AlwaysSampler.java   |  32 +
 .../org/apache/htrace/core/CountSampler.java    |  39 ++
 .../apache/htrace/core/HTraceConfiguration.java | 109 +++
 .../htrace/core/LocalFileSpanReceiver.java      | 257 +++++++
 .../java/org/apache/htrace/core/MilliSpan.java  | 347 ++++++++++
 .../org/apache/htrace/core/NeverSampler.java    |  32 +
 .../java/org/apache/htrace/core/NullScope.java  |  69 ++
 .../apache/htrace/core/POJOSpanReceiver.java    |  49 ++
 .../apache/htrace/core/ProbabilitySampler.java  |  45 ++
 .../java/org/apache/htrace/core/Sampler.java    | 131 ++++
 .../main/java/org/apache/htrace/core/Span.java  | 193 ++++++
 .../java/org/apache/htrace/core/SpanId.java     | 146 ++++
 .../org/apache/htrace/core/SpanReceiver.java    | 164 +++++
 .../htrace/core/StandardOutSpanReceiver.java    |  42 ++
 .../apache/htrace/core/TimelineAnnotation.java  |  40 ++
 .../org/apache/htrace/core/TraceCallable.java   |  56 ++
 .../htrace/core/TraceExecutorService.java       | 121 ++++
 .../org/apache/htrace/core/TraceRunnable.java   |  54 ++
 .../java/org/apache/htrace/core/TraceScope.java | 128 ++++
 .../java/org/apache/htrace/core/Tracer.java     | 673 +++++++++++++++++++
 .../java/org/apache/htrace/core/TracerId.java   | 294 ++++++++
 .../java/org/apache/htrace/core/TracerPool.java | 285 ++++++++
 .../org/apache/htrace/core/TestBadClient.java   | 205 ++++++
 .../apache/htrace/core/TestCountSampler.java    |  41 ++
 .../java/org/apache/htrace/core/TestHTrace.java | 130 ++++
 .../htrace/core/TestHTraceConfiguration.java    |  62 ++
 .../htrace/core/TestLocalFileSpanReceiver.java  |  65 ++
 .../org/apache/htrace/core/TestMilliSpan.java   | 145 ++++
 .../org/apache/htrace/core/TestNullScope.java   |  43 ++
 .../org/apache/htrace/core/TestSampler.java     | 100 +++
 .../java/org/apache/htrace/core/TestSpanId.java |  72 ++
 .../htrace/core/TestSpanReceiverBuilder.java    | 127 ++++
 .../org/apache/htrace/core/TestTracerId.java    |  52 ++
 .../org/apache/htrace/core/TraceCreator.java    | 141 ++++
 .../java/org/apache/htrace/core/TraceGraph.java | 176 +++++
 .../java/org/apache/htrace/util/TestUtil.java   |  91 +++
 pom.xml                                         |   2 +-
 75 files changed, 4882 insertions(+), 4882 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/pom.xml
----------------------------------------------------------------------
diff --git a/htrace-core/pom.xml b/htrace-core/pom.xml
deleted file mode 100644
index 81f2f87..0000000
--- a/htrace-core/pom.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?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-core4</artifactId>
-  <packaging>jar</packaging>
-
-  <parent>
-    <artifactId>htrace</artifactId>
-    <groupId>org.apache.htrace</groupId>
-    <version>4.1.0-incubating-SNAPSHOT</version>
-    <relativePath>..</relativePath>
-  </parent>
-
-  <name>htrace-core</name>
-  <url>http://incubator.apache.org/projects/htrace.html</url>
-
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-  </properties>
-
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-source-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <artifactId>maven-javadoc-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <artifactId>maven-compiler-plugin</artifactId>
-      </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-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>
-      <plugin>
-        <!-- explicitly define maven-deploy-plugin after other to force exec order -->
-        <artifactId>maven-deploy-plugin</artifactId>
-      </plugin>
-    </plugins>
-  </build>
-
-  <dependencies>
-    <!-- Global deps. -->
-    <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-    <!-- core specific deps. -->
-    <dependency>
-      <groupId>commons-logging</groupId>
-      <artifactId>commons-logging</artifactId>
-    </dependency>
-  </dependencies>
-
-  <profiles>
-    <profile>
-      <id>dist</id>
-      <build>
-        <plugins>
-          <plugin>
-            <!--Make it so assembly:single does nothing in here-->
-            <artifactId>maven-assembly-plugin</artifactId>
-            <configuration>
-              <skipAssembly>true</skipAssembly>
-            </configuration>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
-  </profiles>
-</project>

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java
deleted file mode 100644
index 8d5a296..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/AlwaysSampler.java
+++ /dev/null
@@ -1,32 +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.apache.htrace.core;
-
-/**
- * A Sampler that always returns true.
- */
-public final class AlwaysSampler extends Sampler {
-  public static final AlwaysSampler INSTANCE = new AlwaysSampler(null);
-
-  public AlwaysSampler(HTraceConfiguration conf) {
-  }
-
-  @Override
-  public boolean next() {
-    return true;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java
deleted file mode 100644
index 5a838c7..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/CountSampler.java
+++ /dev/null
@@ -1,39 +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.apache.htrace.core;
-
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * 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 extends Sampler {
-  public final static String SAMPLER_FREQUENCY_CONF_KEY = "sampler.frequency";
-
-  final long frequency;
-  long count = ThreadLocalRandom.current().nextLong();
-
-  public CountSampler(HTraceConfiguration conf) {
-    this.frequency = Long.parseLong(conf.get(SAMPLER_FREQUENCY_CONF_KEY), 10);
-  }
-
-  @Override
-  public boolean next() {
-    return (count++ % frequency) == 0;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java b/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
deleted file mode 100644
index c6e445b..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
+++ /dev/null
@@ -1,109 +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.apache.htrace.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Wrapper which integrating applications should implement in order
- * to provide tracing configuration.
- */
-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);
-  }
-
-  public 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);
-
-  public boolean getBoolean(String key, boolean defaultValue) {
-    String value = get(key, String.valueOf(defaultValue)).trim().toLowerCase();
-
-    if ("true".equals(value)) {
-      return true;
-    } else if ("false".equals(value)) {
-      return false;
-    }
-
-    LOG.warn("Expected boolean for key [" + key + "] instead got [" + value + "].");
-    return defaultValue;
-  }
-
-  public int getInt(String key, int defaultVal) {
-    String val = get(key);
-    if (val == null || val.trim().isEmpty()) {
-      return defaultVal;
-    }
-    try {
-      return Integer.parseInt(val);
-    } catch (NumberFormatException nfe) {
-      throw new IllegalArgumentException("Bad value for '" + key + "': should be int");
-    }
-  }
-
-  private static class MapConf extends HTraceConfiguration {
-    private final Map<String, String> conf;
-
-    public MapConf(Map<String, String> conf) {
-      this.conf = new HashMap<String, String>(conf);
-    }
-
-    @Override
-    public String get(String key) {
-      return conf.get(key);
-    }
-
-    @Override
-    public String get(String key, String defaultValue) {
-      String value = get(key);
-      return value == null ? defaultValue : value;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
deleted file mode 100644
index 69a43b1..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
+++ /dev/null
@@ -1,257 +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.apache.htrace.core;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectWriter;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.file.FileSystems;
-import java.nio.file.StandardOpenOption;
-import java.util.UUID;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Writes the spans it receives to a local file.
- */
-public class LocalFileSpanReceiver extends SpanReceiver {
-  private static final Log LOG = LogFactory.getLog(LocalFileSpanReceiver.class);
-  public static final String PATH_KEY = "local.file.span.receiver.path";
-  public static final String CAPACITY_KEY = "local.file.span.receiver.capacity";
-  public static final int CAPACITY_DEFAULT = 5000;
-  private static ObjectWriter JSON_WRITER = new ObjectMapper().writer();
-  private final String path;
-
-  private byte[][] bufferedSpans;
-  private int bufferedSpansIndex;
-  private final ReentrantLock bufferLock = new ReentrantLock();
-
-  private final FileOutputStream stream;
-  private final FileChannel channel;
-  private final ReentrantLock channelLock = new ReentrantLock();
-
-  public LocalFileSpanReceiver(HTraceConfiguration conf) {
-    int capacity = conf.getInt(CAPACITY_KEY, CAPACITY_DEFAULT);
-    if (capacity < 1) {
-      throw new IllegalArgumentException(CAPACITY_KEY + " must not be " +
-          "less than 1.");
-    }
-    String pathStr = conf.get(PATH_KEY);
-    if (pathStr == null || pathStr.isEmpty()) {
-      path = getUniqueLocalTraceFileName();
-    } else {
-      path = pathStr;
-    }
-    boolean success = false;
-    try {
-      this.stream = new FileOutputStream(path, true);
-    } catch (IOException ioe) {
-      LOG.error("Error opening " + path + ": " + ioe.getMessage());
-      throw new RuntimeException(ioe);
-    }
-    this.channel = stream.getChannel();
-    if (this.channel == null) {
-      try {
-        this.stream.close();
-      } catch (IOException e) {
-        LOG.error("Error closing " + path, e);
-      }
-      LOG.error("Failed to get channel for " + path);
-      throw new RuntimeException("Failed to get channel for " + path);
-    }
-    this.bufferedSpans = new byte[capacity][];
-    this.bufferedSpansIndex = 0;
-    if (LOG.isDebugEnabled()) {
-      LOG.debug("Created new LocalFileSpanReceiver with path = " + path +
-                ", capacity = " + capacity);
-    }
-  }
-
-  /**
-   * Number of buffers to use in FileChannel#write.
-   *
-   * On UNIX, FileChannel#write uses writev-- a kernel interface that allows
-   * us to send multiple buffers at once.  This is more efficient than making a
-   * separate write call for each buffer, since it minimizes the number of
-   * transitions from userspace to kernel space.
-   */
-  private final int WRITEV_SIZE = 20;
-
-  private final static ByteBuffer newlineBuf = 
-      ByteBuffer.wrap(new byte[] { (byte)0xa });
-
-  /**
-   * Flushes a bufferedSpans array.
-   */
-  private void doFlush(byte[][] toFlush, int len) throws IOException {
-    int bidx = 0, widx = 0;
-    ByteBuffer writevBufs[] = new ByteBuffer[2 * WRITEV_SIZE];
-
-    while (true) {
-      if (widx == writevBufs.length) {
-        channel.write(writevBufs);
-        widx = 0;
-      }
-      if (bidx == len) {
-        break;
-      }
-      writevBufs[widx] = ByteBuffer.wrap(toFlush[bidx]);
-      writevBufs[widx + 1] = newlineBuf;
-      bidx++;
-      widx+=2;
-    }
-    if (widx > 0) {
-      channel.write(writevBufs, 0, widx);
-    }
-  }
-
-  @Override
-  public void receiveSpan(Span span) {
-    // Serialize the span data into a byte[].  Note that we're not holding the
-    // lock here, to improve concurrency.
-    byte jsonBuf[] = null;
-    try {
-      jsonBuf = JSON_WRITER.writeValueAsBytes(span);
-    } catch (JsonProcessingException e) {
-        LOG.error("receiveSpan(path=" + path + ", span=" + span + "): " +
-                  "Json processing error: " + e.getMessage());
-      return;
-    }
-
-    // Grab the bufferLock and put our jsonBuf into the list of buffers to
-    // flush. 
-    byte toFlush[][] = null;
-    bufferLock.lock();
-    try {
-      if (bufferedSpans == null) {
-        LOG.debug("receiveSpan(path=" + path + ", span=" + span + "): " +
-                  "LocalFileSpanReceiver for " + path + " is closed.");
-        return;
-      }
-      bufferedSpans[bufferedSpansIndex] = jsonBuf;
-      bufferedSpansIndex++;
-      if (bufferedSpansIndex == bufferedSpans.length) {
-        // If we've hit the limit for the number of buffers to flush, 
-        // swap out the existing bufferedSpans array for a new array, and
-        // prepare to flush those spans to disk.
-        toFlush = bufferedSpans;
-        bufferedSpansIndex = 0;
-        bufferedSpans = new byte[bufferedSpans.length][];
-      }
-    } finally {
-      bufferLock.unlock();
-    }
-    if (toFlush != null) {
-      // We released the bufferLock above, to avoid blocking concurrent
-      // receiveSpan calls.  But now, we must take the channelLock, to make
-      // sure that we have sole access to the output channel.  If we did not do
-      // this, we might get interleaved output.
-      //
-      // There is a small chance that another thread doing a flush of more
-      // recent spans could get ahead of us here, and take the lock before we
-      // do.  This is ok, since spans don't have to be written out in order.
-      channelLock.lock();
-      try {
-        doFlush(toFlush, toFlush.length);
-      } catch (IOException ioe) {
-        LOG.error("Error flushing buffers to " + path + ": " +
-            ioe.getMessage());
-      } finally {
-        channelLock.unlock();
-      }
-    }
-  }
-
-  @Override
-  public void close() throws IOException {
-    byte toFlush[][] = null;
-    int numToFlush = 0;
-    bufferLock.lock();
-    try {
-      if (bufferedSpans == null) {
-        LOG.info("LocalFileSpanReceiver for " + path + " was already closed.");
-        return;
-      }
-      numToFlush = bufferedSpansIndex;
-      bufferedSpansIndex = 0;
-      toFlush = bufferedSpans;
-      bufferedSpans = null;
-    } finally {
-      bufferLock.unlock();
-    }
-    channelLock.lock();
-    try {
-      doFlush(toFlush, numToFlush);
-    } catch (IOException ioe) {
-      LOG.error("Error flushing buffers to " + path + ": " +
-          ioe.getMessage());
-    } finally {
-      try {
-        stream.close();
-      } catch (IOException e) {
-        LOG.error("Error closing stream for " + path, e);
-      }
-      channelLock.unlock();
-    }
-  }
-
-  public static String getUniqueLocalTraceFileName() {
-    String tmp = System.getProperty("java.io.tmpdir", "/tmp");
-    String nonce = null;
-    BufferedReader reader = null;
-    try {
-      // On Linux we can get a unique local file name by reading the process id
-      // out of /proc/self/stat.  (There isn't any portable way to get the
-      // process ID from Java.)
-      reader = new BufferedReader(
-          new InputStreamReader(new FileInputStream("/proc/self/stat"),
-                                "UTF-8"));
-      String line = reader.readLine();
-      if (line == null) {
-        throw new EOFException();
-      }
-      nonce = line.split(" ")[0];
-    } catch (IOException e) {
-    } finally {
-      if (reader != null) {
-        try {
-          reader.close();
-        } catch(IOException e) {
-          LOG.warn("Exception in closing " + reader, e);
-        }
-      }
-    }
-    if (nonce == null) {
-      // If we can't use the process ID, use a random nonce.
-      nonce = UUID.randomUUID().toString();
-    }
-    return new File(tmp, nonce).getAbsolutePath();
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java b/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java
deleted file mode 100644
index 5dd6bdb..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/MilliSpan.java
+++ /dev/null
@@ -1,347 +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.apache.htrace.core;
-
-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.ObjectMapper;
-import com.fasterxml.jackson.databind.ObjectReader;
-import com.fasterxml.jackson.databind.ObjectWriter;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-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;
-
-/**
- * A Span implementation that stores its information in milliseconds since the
- * epoch.
- */
-@JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class)
-public class MilliSpan implements Span {
-  private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
-  private static ObjectReader JSON_READER = OBJECT_MAPPER.reader(MilliSpan.class);
-  private static ObjectWriter JSON_WRITER = OBJECT_MAPPER.writer();
-  private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0];
-  private static final String EMPTY_STRING = "";
-
-  private long begin;
-  private long end;
-  private final String description;
-  private SpanId parents[];
-  private final SpanId spanId;
-  private Map<String, String> traceInfo = null;
-  private String tracerId;
-  private List<TimelineAnnotation> timeline = null;
-
-  @Override
-  public Span child(String childDescription) {
-    return new MilliSpan.Builder().
-      begin(System.currentTimeMillis()).
-      end(0).
-      description(childDescription).
-      parents(new SpanId[] {spanId}).
-      spanId(spanId.newChildId()).
-      tracerId(tracerId).
-      build();
-  }
-
-  /**
-   * The public interface for constructing a MilliSpan.
-   */
-  public static class Builder {
-    private long begin;
-    private long end;
-    private String description = EMPTY_STRING;
-    private SpanId parents[] = EMPTY_PARENT_ARRAY;
-    private SpanId spanId = SpanId.INVALID;
-    private Map<String, String> traceInfo = null;
-    private String tracerId = EMPTY_STRING;
-    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 parents(SpanId parents[]) {
-      this.parents = parents;
-      return this;
-    }
-
-    public Builder parents(List<SpanId> parentList) {
-      SpanId[] parents = new SpanId[parentList.size()];
-      for (int i = 0; i < parentList.size(); i++) {
-        parents[i] = parentList.get(i);
-      }
-      this.parents = parents;
-      return this;
-    }
-
-    public Builder spanId(SpanId spanId) {
-      this.spanId = spanId;
-      return this;
-    }
-
-    public Builder traceInfo(Map<String, String> traceInfo) {
-      this.traceInfo = traceInfo.isEmpty() ? null : traceInfo;
-      return this;
-    }
-
-    public Builder tracerId(String tracerId) {
-      this.tracerId = tracerId;
-      return this;
-    }
-
-    public Builder timeline(List<TimelineAnnotation> timeline) {
-      this.timeline = timeline.isEmpty() ? null : timeline;
-      return this;
-    }
-
-    public MilliSpan build() {
-      return new MilliSpan(this);
-    }
-  }
-
-  public MilliSpan() {
-    this.begin = 0;
-    this.end = 0;
-    this.description = EMPTY_STRING;
-    this.parents = EMPTY_PARENT_ARRAY;
-    this.spanId = SpanId.INVALID;
-    this.traceInfo = null;
-    this.tracerId = EMPTY_STRING;
-    this.timeline = null;
-  }
-
-  private MilliSpan(Builder builder) {
-    this.begin = builder.begin;
-    this.end = builder.end;
-    this.description = builder.description;
-    this.parents = builder.parents;
-    this.spanId = builder.spanId;
-    this.traceInfo = builder.traceInfo;
-    this.tracerId = builder.tracerId;
-    this.timeline = builder.timeline;
-  }
-
-  @Override
-  public synchronized void stop() {
-    if (end == 0) {
-      if (begin == 0)
-        throw new IllegalStateException("Span for " + description
-            + " has not been started");
-      end = System.currentTimeMillis();
-    }
-  }
-
-  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 SpanId getSpanId() {
-    return spanId;
-  }
-
-  @Override
-  public SpanId[] getParents() {
-    return parents;
-  }
-
-  @Override
-  public void setParents(SpanId[] parents) {
-    this.parents = parents;
-  }
-
-  @Override
-  public long getStartTimeMillis() {
-    return begin;
-  }
-
-  @Override
-  public long getStopTimeMillis() {
-    return end;
-  }
-
-  @Override
-  public void addKVAnnotation(String key, String value) {
-    if (traceInfo == null)
-      traceInfo = new HashMap<String, String>();
-    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<String, String> 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 getTracerId() {
-    return tracerId;
-  }
-
-  @Override
-  public void setTracerId(String tracerId) {
-    this.tracerId = tracerId;
-  }
-
-  @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();
-  }
-
-  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();
-      JsonNode bNode = root.get("b");
-      if (bNode != null) {
-        builder.begin(bNode.asLong());
-      }
-      JsonNode eNode = root.get("e");
-      if (eNode != null) {
-        builder.end(eNode.asLong());
-      }
-      JsonNode dNode = root.get("d");
-      if (dNode != null) {
-        builder.description(dNode.asText());
-      }
-      JsonNode sNode = root.get("a");
-      if (sNode != null) {
-        builder.spanId(SpanId.fromString(sNode.asText()));
-      }
-      JsonNode rNode = root.get("r");
-      if (rNode != null) {
-        builder.tracerId(rNode.asText());
-      }
-      JsonNode parentsNode = root.get("p");
-      LinkedList<SpanId> parents = new LinkedList<SpanId>();
-      if (parentsNode != null) {
-        for (Iterator<JsonNode> iter = parentsNode.elements();
-             iter.hasNext(); ) {
-          JsonNode parentIdNode = iter.next();
-          parents.add(SpanId.fromString(parentIdNode.asText()));
-        }
-      }
-      builder.parents(parents);
-      JsonNode traceInfoNode = root.get("n");
-      if (traceInfoNode != null) {
-        HashMap<String, String> traceInfo = new HashMap<String, String>();
-        for (Iterator<String> iter = traceInfoNode.fieldNames();
-             iter.hasNext(); ) {
-          String field = iter.next();
-          traceInfo.put(field, traceInfoNode.get(field).asText());
-        }
-        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();
-    }
-  }
-
-  public static MilliSpan fromJson(String json) throws IOException {
-    return JSON_READER.readValue(json);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java b/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java
deleted file mode 100644
index 60cc7d2..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/NeverSampler.java
+++ /dev/null
@@ -1,32 +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.apache.htrace.core;
-
-/**
- * A Sampler that never returns true.
- */
-public final class NeverSampler extends Sampler {
-  public static final NeverSampler INSTANCE = new NeverSampler(null);
-
-  public NeverSampler(HTraceConfiguration conf) {
-  }
-
-  @Override
-  public boolean next() {
-    return false;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java b/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java
deleted file mode 100644
index fe76e46..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/NullScope.java
+++ /dev/null
@@ -1,69 +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.apache.htrace.core;
-
-/**
- * An empty {@link TraceScope}.
- */
-class NullScope extends TraceScope {
-  NullScope(Tracer tracer) {
-    super(tracer, null, null);
-  }
-
-  @Override
-  public SpanId getSpanId() {
-    return SpanId.INVALID;
-  }
-
-  @Override
-  public void detach() {
-    if (detached) {
-      Tracer.throwClientError("Can't detach this TraceScope  because " +
-          "it is already detached.");
-    }
-    detached = true;
-  }
-
-  @Override
-  public void reattach() {
-    if (!detached) {
-      Tracer.throwClientError("Can't reattach this TraceScope  because " +
-          "it is not detached.");
-    }
-    detached = false;
-  }
-
-  @Override
-  public void close() {
-    tracer.popNullScope();
-  }
-
-  @Override
-  public String toString() {
-    return "NullScope";
-  }
-
-  @Override
-  public void addKVAnnotation(String key, String value) {
-    // do nothing
-  }
-
-  @Override
-  public void addTimelineAnnotation(String msg) {
-    // do nothing
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
deleted file mode 100644
index 34322fa..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
+++ /dev/null
@@ -1,49 +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.apache.htrace.core;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashSet;
-
-/**
- * SpanReceiver for testing only that just collects the Span objects it
- * receives. The spans it receives can be accessed with getSpans();
- */
-public class POJOSpanReceiver extends SpanReceiver {
-  private final Collection<Span> spans;
-
-  public POJOSpanReceiver(HTraceConfiguration conf) {
-    this.spans = new HashSet<Span>();
-  }
-
-  /**
-   * @return The spans this POJOSpanReceiver has received.
-   */
-  public Collection<Span> getSpans() {
-    return spans;
-  }
-
-  @Override
-  public void close() throws IOException {
-  }
-
-  @Override
-  public void receiveSpan(Span span) {
-    spans.add(span);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java b/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
deleted file mode 100644
index c0bb16c..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
+++ /dev/null
@@ -1,45 +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.apache.htrace.core;
-
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * 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 extends Sampler {
-  private static final Log LOG = LogFactory.getLog(ProbabilitySampler.class);
-  public final double threshold;
-  public final static String SAMPLER_FRACTION_CONF_KEY = "sampler.fraction";
-
-  public ProbabilitySampler(HTraceConfiguration conf) {
-    this.threshold = Double.parseDouble(conf.get(SAMPLER_FRACTION_CONF_KEY));
-    if (LOG.isTraceEnabled()) {
-      LOG.trace("Created new ProbabilitySampler with threshold = " +
-                threshold + ".");
-    }
-  }
-
-  @Override
-  public boolean next() {
-    return ThreadLocalRandom.current().nextDouble() < threshold;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java b/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java
deleted file mode 100644
index af0165c..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/Sampler.java
+++ /dev/null
@@ -1,131 +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.apache.htrace.core;
-
-import java.lang.reflect.Constructor;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Extremely simple callback to determine the frequency that an action should be
- * performed.
- * <p/>
- * For example, the next() function may look like this:
- * <p/>
- * <pre>
- * <code>
- * public boolean next() {
- *   return Math.random() &gt; 0.5;
- * }
- * </code>
- * </pre>
- * This would trace 50% of all gets, 75% of all puts and would not trace any other requests.
- */
-public abstract class Sampler {
-  /**
-   * A {@link Sampler} builder. It takes a {@link Sampler} class name and
-   * constructs an instance of that class, with the provided configuration.
-   */
-  public static class Builder {
-    private static final Log LOG = LogFactory.getLog(Builder.class);
-
-    private final static String DEFAULT_PACKAGE = "org.apache.htrace.core";
-    private final HTraceConfiguration conf;
-    private String className;
-    private ClassLoader classLoader = Builder.class.getClassLoader();
-
-    public Builder(HTraceConfiguration conf) {
-      this.conf = conf;
-      reset();
-    }
-
-    public Builder reset() {
-      this.className = null;
-      return this;
-    }
-
-    public Builder className(String className) {
-      this.className = className;
-      return this;
-    }
-
-    public Builder classLoader(ClassLoader classLoader) {
-      this.classLoader = classLoader;
-      return this;
-    }
-
-    private void throwError(String errorStr) {
-      LOG.error(errorStr);
-      throw new RuntimeException(errorStr);
-    }
-
-    private void throwError(String errorStr, Throwable e) {
-      LOG.error(errorStr, e);
-      throw new RuntimeException(errorStr, e);
-    }
-
-    public Sampler build() {
-      Sampler sampler = newSampler();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("Created new sampler of type " +
-            sampler.getClass().getName(), new Exception());
-      }
-      return sampler;
-    }
-
-    private Sampler newSampler() {
-      if (className == null || className.isEmpty()) {
-        throwError("No sampler class specified.");
-      }
-      String str = className;
-      if (!str.contains(".")) {
-        str = DEFAULT_PACKAGE + "." + str;
-      }
-      Class cls = null;
-      try {
-        cls = classLoader.loadClass(str);
-      } catch (ClassNotFoundException e) {
-        throwError("Cannot find Sampler class " + str);
-      }
-      Constructor<Sampler> ctor = null;
-      try {
-        ctor = cls.getConstructor(HTraceConfiguration.class);
-      } catch (NoSuchMethodException e) {
-        throwError("Cannot find a constructor for class " +
-            str + "which takes an HTraceConfiguration.");
-      }
-      Sampler sampler = null;
-      try {
-        LOG.debug("Creating new instance of " + str + "...");
-        sampler = ctor.newInstance(conf);
-      } catch (ReflectiveOperationException e) {
-        throwError("Reflection error when constructing " +
-            str + ".", e);
-      } catch (Throwable t) {
-        throwError("NewInstance error when constructing " +
-            str + ".", t);
-      }
-      return sampler;
-    }
-  }
-
-  public static final Sampler ALWAYS = AlwaysSampler.INSTANCE;
-  public static final Sampler NEVER = NeverSampler.INSTANCE;
-
-  public abstract boolean next();
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/Span.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Span.java b/htrace-core/src/main/java/org/apache/htrace/core/Span.java
deleted file mode 100644
index e63d414..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/Span.java
+++ /dev/null
@@ -1,193 +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.apache.htrace.core;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-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;
-
-/**
- * Base interface for gathering and reporting statistics about a block of
- * execution.
- * <p/>
- * Spans should form a directed acyclic graph structure.  It should be possible
- * to keep following the parents of a span until you arrive at a span with no
- * parents.<p/>
- */
-@JsonSerialize(using = Span.SpanSerializer.class)
-public interface Span {
-  /**
-   * The block has completed, stop the clock
-   */
-  void stop();
-
-  /**
-   * Get the start time, in milliseconds
-   */
-  long getStartTimeMillis();
-
-  /**
-   * Get the stop time, in milliseconds
-   */
-  long getStopTimeMillis();
-
-  /**
-   * Return the total amount of time elapsed since start was called, if running,
-   * or difference between stop and start
-   */
-  long getAccumulatedMillis();
-
-  /**
-   * Has the span been started and not yet stopped?
-   */
-  boolean isRunning();
-
-  /**
-   * Return a textual description of this span.<p/>
-   *
-   * Will never be null.
-   */
-  String getDescription();
-
-  /**
-   * A pseudo-unique (random) number assigned to this span instance.<p/>
-   *
-   * The spanId is immutable and cannot be changed.  It is safe to access this
-   * from multiple threads.
-   */
-  SpanId getSpanId();
-
-  /**
-   * Create a child span of this span with the given description
-   * @deprecated Since 4.0.0. Use {@link MilliSpan.Builder}
-   */
-  @Deprecated
-  Span child(String description);
-
-  @Override
-  String toString();
-
-  /**
-   * Returns the parent IDs of the span.<p/>
-   *
-   * The array will be empty if there are no parents.
-   */
-  SpanId[] getParents();
-
-  /**
-   * Set the parents of this span.<p/>
-   *
-   * Any existing parents will be cleared by this call.
-   */
-  void setParents(SpanId[] parents);
-
-  /**
-   * Add a data annotation associated with this span
-   */
-  void addKVAnnotation(String key, String value);
-
-  /**
-   * Add a timeline annotation associated with this span
-   */
-  void addTimelineAnnotation(String msg);
-
-  /**
-   * Get data associated with this span (read only)<p/>
-   *
-   * Will never be null.
-   */
-  Map<String, String> getKVAnnotations();
-
-  /**
-   * Get any timeline annotations (read only)<p/>
-   *
-   * Will never be null.
-   */
-  List<TimelineAnnotation> getTimelineAnnotations();
-
-  /**
-   * Return a unique id for the process from which this Span originated.<p/>
-   *
-   * Will never be null.
-   */
-  String getTracerId();
-
-  /**
-   * Set the process id of a span.
-   */
-  void setTracerId(String s);
-
-  /**
-   * 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();
-      if (span.getSpanId().isValid()) {
-        jgen.writeStringField("a", span.getSpanId().toString());
-      }
-      if (span.getStartTimeMillis() != 0) {
-        jgen.writeNumberField("b", span.getStartTimeMillis());
-      }
-      if (span.getStopTimeMillis() != 0) {
-        jgen.writeNumberField("e", span.getStopTimeMillis());
-      }
-      if (!span.getDescription().isEmpty()) {
-        jgen.writeStringField("d", span.getDescription());
-      }
-      String tracerId = span.getTracerId();
-      if (!tracerId.isEmpty()) {
-        jgen.writeStringField("r", tracerId);
-      }
-      jgen.writeArrayFieldStart("p");
-      for (SpanId parent : span.getParents()) {
-        jgen.writeString(parent.toString());
-      }
-      jgen.writeEndArray();
-      Map<String, String> traceInfoMap = span.getKVAnnotations();
-      if (!traceInfoMap.isEmpty()) {
-        jgen.writeObjectFieldStart("n");
-        for (Map.Entry<String, String> e : traceInfoMap.entrySet()) {
-          jgen.writeStringField(e.getKey(), e.getValue());
-        }
-        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();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java b/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java
deleted file mode 100644
index ed31ad3..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/SpanId.java
+++ /dev/null
@@ -1,146 +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.apache.htrace.core;
-
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * Uniquely identifies an HTrace span.
- *
- * Span IDs are 128 bits in total.  The upper 64 bits of a span ID is the same
- * as the upper 64 bits of the parent span, if there is one.  The lower 64 bits
- * are always random.
- */
-public final class SpanId implements Comparable<SpanId> {
-  private static final int SPAN_ID_STRING_LENGTH = 32;
-  private final long high;
-  private final long low;
-
-  /**
-   * The invalid span ID, which is all zeroes.
-   *
-   * It is also the "least" span ID in the sense that it is considered
-   * smaller than any other span ID.
-   */
-  public static SpanId INVALID = new SpanId(0, 0);
-
-  private static long nonZeroRand64() {
-    while (true) {
-      long r = ThreadLocalRandom.current().nextLong();
-      if (r != 0) {
-        return r;
-      }
-    }
-  }
-
-  public static SpanId fromRandom() {
-    return new SpanId(nonZeroRand64(), nonZeroRand64());
-  }
-
-  public static SpanId fromString(String str) {
-    if (str.length() != SPAN_ID_STRING_LENGTH) {
-      throw new RuntimeException("Invalid SpanID string: length was not " +
-          SPAN_ID_STRING_LENGTH);
-    }
-    long high =
-      ((Long.parseLong(str.substring(0, 8), 16)) << 32) |
-      (Long.parseLong(str.substring(8, 16), 16));
-    long low =
-      ((Long.parseLong(str.substring(16, 24), 16)) << 32) |
-      (Long.parseLong(str.substring(24, 32), 16));
-    return new SpanId(high, low);
-  }
-
-  public SpanId(long high, long low) {
-    this.high = high;
-    this.low = low;
-  }
-
-  public long getHigh() {
-    return high;
-  }
-
-  public long getLow() {
-    return low;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (!(o instanceof SpanId)) {
-      return false;
-    }
-    SpanId other = (SpanId)o;
-    return ((other.high == high) && (other.low == low));
-  }
-
-  @Override
-  public int compareTo(SpanId other) {
-    int cmp = compareAsUnsigned(high, other.high);
-    if (cmp != 0) {
-      return cmp;
-    }
-    return compareAsUnsigned(low, other.low);
-  }
-
-  private static int compareAsUnsigned(long a, long b) {
-    boolean aSign = a < 0;
-    boolean bSign = b < 0;
-    if (aSign != bSign) {
-      if (aSign) {
-        return 1;
-      } else {
-        return -1;
-      }
-    }
-    if (aSign) {
-      a = -a;
-      b = -b;
-    }
-    if (a < b) {
-      return -1;
-    } else if (a > b) {
-      return 1;
-    } else {
-      return 0;
-    }
-  }
-
-  @Override
-  public int hashCode() {
-    return (int)((0xffffffff & (high >> 32))) ^
-           (int)((0xffffffff & (high >> 0))) ^
-           (int)((0xffffffff & (low >> 32))) ^
-           (int)((0xffffffff & (low >> 0)));
-  }
-
-  @Override
-  public String toString() {
-    return String.format("%08x%08x%08x%08x",
-        (0x00000000ffffffffL & (high >> 32)),
-        (0x00000000ffffffffL & high),
-        (0x00000000ffffffffL & (low >> 32)),
-        (0x00000000ffffffffL & low));
-  }
-
-  public boolean isValid() {
-    return (high != 0)  || (low != 0);
-  }
-
-  public SpanId newChildId() {
-    return new SpanId(high, nonZeroRand64());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java
deleted file mode 100644
index a955ddf..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/SpanReceiver.java
+++ /dev/null
@@ -1,164 +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.apache.htrace.core;
-
-import java.io.Closeable;
-import java.lang.reflect.Constructor;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * 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>
- */
-public abstract class SpanReceiver implements Closeable {
-  /**
-   * A {@link SpanReceiver} builder. It takes a {@link SpanReceiver} class name
-   * and constructs an instance of that class, with the provided configuration.
-   */
-  public static class Builder {
-    private static final Log LOG = LogFactory.getLog(Builder.class);
-
-    private final static String DEFAULT_PACKAGE = "org.apache.htrace.core";
-    private final HTraceConfiguration conf;
-    private boolean logErrors;
-    private String className;
-    private ClassLoader classLoader = Builder.class.getClassLoader();
-
-    public Builder(HTraceConfiguration conf) {
-      this.conf = conf;
-      reset();
-    }
-
-    /**
-     * Set this builder back to defaults.
-     *
-     * @return this instance.
-     */
-    public Builder reset() {
-      this.logErrors = true;
-      this.className = null;
-      return this;
-    }
-
-    public Builder className(final String className) {
-      this.className = className;
-      return this;
-    }
-
-    /**
-     * Configure whether we should log errors during build().
-     * @return This instance
-     */
-    public Builder logErrors(boolean logErrors) {
-      this.logErrors = logErrors;
-      return this;
-    }
-
-    public Builder classLoader(ClassLoader classLoader) {
-      this.classLoader = classLoader;
-      return this;
-    }
-
-    private void throwError(String errorStr) {
-      if (logErrors) {
-        LOG.error(errorStr);
-      }
-      throw new RuntimeException(errorStr);
-    }
-
-    private void throwError(String errorStr, Throwable e) {
-      if (logErrors) {
-        LOG.error(errorStr, e);
-      }
-      throw new RuntimeException(errorStr, e);
-    }
-
-    public SpanReceiver build() {
-      SpanReceiver spanReceiver = newSpanReceiver();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("Created new span receiver of type " +
-                  spanReceiver.getClass().getName());
-      }
-      return spanReceiver;
-    }
-
-    private SpanReceiver newSpanReceiver() {
-      if ((className == null) || className.isEmpty()) {
-        throwError("No span receiver class specified.");
-      }
-      String str = className;
-      if (!str.contains(".")) {
-        str = DEFAULT_PACKAGE + "." + str;
-      }
-      Class cls = null;
-      try {
-        cls = classLoader.loadClass(str);
-      } catch (ClassNotFoundException e) {
-        throwError("Cannot find SpanReceiver class " + str);
-      }
-      Constructor<SpanReceiver> ctor = null;
-      try {
-        ctor = cls.getConstructor(HTraceConfiguration.class);
-      } catch (NoSuchMethodException e) {
-        throwError("Cannot find a constructor for class " +
-            str + "which takes an HTraceConfiguration.");
-      }
-      SpanReceiver receiver = null;
-      try {
-        LOG.debug("Creating new instance of " + str + "...");
-        receiver = ctor.newInstance(conf);
-      } catch (ReflectiveOperationException e) {
-        throwError("Reflection error when constructing " +
-            str + ".", e);
-      } catch (Throwable t) {
-        throwError("NewInstance error when constructing " +
-            str + ".", t);
-      }
-      return receiver;
-    }
-  }
-
-  /**
-   * An ID which uniquely identifies this SpanReceiver.
-   */
-  private final long id;
-
-  private static final AtomicLong HIGHEST_SPAN_RECEIVER_ID = new AtomicLong(0);
-
-  /**
-   * Get an ID uniquely identifying this SpanReceiver.
-   */
-  public final long getId() {
-    return id;
-  }
-
-  protected SpanReceiver() {
-    this.id = HIGHEST_SPAN_RECEIVER_ID.incrementAndGet();
-  }
-
-  /**
-   * Called when a Span is stopped and can now be stored.
-   */
-  public abstract void receiveSpan(Span span);
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java b/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
deleted file mode 100644
index f443ec6..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
+++ /dev/null
@@ -1,42 +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.apache.htrace.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.io.IOException;
-
-/**
- * Used for testing. Simply prints to standard out any spans it receives.
- */
-public class StandardOutSpanReceiver extends SpanReceiver {
-  private static final Log LOG = LogFactory.getLog(StandardOutSpanReceiver.class);
-
-  public StandardOutSpanReceiver(HTraceConfiguration conf) {
-    LOG.trace("Created new StandardOutSpanReceiver.");
-  }
-
-  @Override
-  public void receiveSpan(Span span) {
-    System.out.println(span);
-  }
-
-  @Override
-  public void close() throws IOException {
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java b/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
deleted file mode 100644
index 18de061..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
+++ /dev/null
@@ -1,40 +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.apache.htrace.core;
-
-public class TimelineAnnotation {
-  private final long time;
-  private final String msg;
-
-  public TimelineAnnotation(long time, String msg) {
-    this.time = time;
-    this.msg = msg;
-  }
-
-  public long getTime() {
-    return time;
-  }
-
-  public String getMessage() {
-    return msg;
-  }
-
-  @Override
-  public String toString() {
-    return "@" + time + ": " + msg;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java
deleted file mode 100644
index a0fec17..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TraceCallable.java
+++ /dev/null
@@ -1,56 +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.apache.htrace.core;
-
-import java.util.concurrent.Callable;
-
-/**
- * Wrap a Callable with a Span that survives a change in threads.
- */
-public class TraceCallable<V> implements Callable<V> {
-  private final Tracer tracer;
-  private final Callable<V> impl;
-  private final TraceScope parent;
-  private final String description;
-
-  TraceCallable(Tracer tracer, TraceScope parent, Callable<V> impl,
-      String description) {
-    this.tracer = tracer;
-    this.impl = impl;
-    this.parent = parent;
-    if (description == null) {
-      this.description = Thread.currentThread().getName();
-    } else {
-      this.description = description;
-    }
-  }
-
-  @Override
-  public V call() throws Exception {
-    TraceScope chunk = tracer.newScope(description,
-        parent.getSpan().getSpanId());
-    try {
-      return impl.call();
-    } finally {
-      chunk.close();
-    }
-  }
-
-  public Callable<V> getImpl() {
-    return impl;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java
deleted file mode 100644
index 81e31ea..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TraceExecutorService.java
+++ /dev/null
@@ -1,121 +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.apache.htrace.core;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-public class TraceExecutorService implements ExecutorService {
-  private final Tracer tracer;
-  private final String scopeName;
-  private final ExecutorService impl;
-
-  TraceExecutorService(Tracer tracer, String scopeName,
-                       ExecutorService impl) {
-    this.tracer = tracer;
-    this.scopeName = scopeName;
-    this.impl = impl;
-  }
-
-  @Override
-  public void execute(Runnable command) {
-    impl.execute(tracer.wrap(command, scopeName));
-  }
-
-  @Override
-  public void shutdown() {
-    impl.shutdown();
-  }
-
-  @Override
-  public List<Runnable> shutdownNow() {
-    return impl.shutdownNow();
-  }
-
-  @Override
-  public boolean isShutdown() {
-    return impl.isShutdown();
-  }
-
-  @Override
-  public boolean isTerminated() {
-    return impl.isTerminated();
-  }
-
-  @Override
-  public boolean awaitTermination(long timeout, TimeUnit unit)
-      throws InterruptedException {
-    return impl.awaitTermination(timeout, unit);
-  }
-
-  @Override
-  public <T> Future<T> submit(Callable<T> task) {
-    return impl.submit(tracer.wrap(task, scopeName));
-  }
-
-  @Override
-  public <T> Future<T> submit(Runnable task, T result) {
-    return impl.submit(tracer.wrap(task, scopeName), result);
-  }
-
-  @Override
-  public Future<?> submit(Runnable task) {
-    return impl.submit(tracer.wrap(task, scopeName));
-  }
-
-  private <T> Collection<? extends Callable<T>> wrapCollection(
-      Collection<? extends Callable<T>> tasks) {
-    List<Callable<T>> result = new ArrayList<Callable<T>>();
-    for (Callable<T> task : tasks) {
-      result.add(tracer.wrap(task, scopeName));
-    }
-    return result;
-  }
-
-  @Override
-  public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
-      throws InterruptedException {
-    return impl.invokeAll(wrapCollection(tasks));
-  }
-
-  @Override
-  public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
-                                       long timeout, TimeUnit unit) throws InterruptedException {
-    return impl.invokeAll(wrapCollection(tasks), timeout, unit);
-  }
-
-  @Override
-  public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
-      throws InterruptedException, ExecutionException {
-    return impl.invokeAny(wrapCollection(tasks));
-  }
-
-  @Override
-  public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout,
-                         TimeUnit unit) throws InterruptedException, ExecutionException,
-      TimeoutException {
-    return impl.invokeAny(wrapCollection(tasks), timeout, unit);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java
deleted file mode 100644
index 8f98708..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TraceRunnable.java
+++ /dev/null
@@ -1,54 +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.apache.htrace.core;
-
-/**
- * Wrap a Runnable with a Span that survives a change in threads.
- */
-public class TraceRunnable implements Runnable {
-  private final Tracer tracer;
-  private final TraceScope parent;
-  private final Runnable runnable;
-  private final String description;
-
-  public TraceRunnable(Tracer tracer, TraceScope parent,
-      Runnable runnable, String description) {
-    this.tracer = tracer;
-    this.parent = parent;
-    this.runnable = runnable;
-    if (description == null) {
-      this.description = Thread.currentThread().getName();
-    } else {
-      this.description = description;
-    }
-  }
-
-  @Override
-  public void run() {
-    TraceScope chunk = tracer.newScope(description,
-        parent.getSpan().getSpanId());
-    try {
-      runnable.run();
-    } finally {
-      chunk.close();
-    }
-  }
-
-  public Runnable getRunnable() {
-    return runnable;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java b/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java
deleted file mode 100644
index 05a053e..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TraceScope.java
+++ /dev/null
@@ -1,128 +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.apache.htrace.core;
-
-import java.io.Closeable;
-
-/**
- * Create a new TraceScope at major transitions. Hosts current tracing context.
- */
-public class TraceScope implements Closeable {
-  /**
-   * The tracer to use for this scope.
-   */
-  final Tracer tracer;
-
-  /**
-   * The trace span for this scope, or null if the scope is closed.
-   *
-   * If the scope is closed, it must also be detached.
-   */
-  private final Span span;
-
-  /**
-   * The parent of this trace scope, or null if there is no parent.
-   */
-  private TraceScope parent;
-
-  /**
-   * True if this scope is detached.
-   */
-  boolean detached;
-
-  TraceScope(Tracer tracer, Span span, TraceScope parent) {
-    this.tracer = tracer;
-    this.span = span;
-    this.parent = parent;
-    this.detached = false;
-  }
-
-  /**
-   * Returns the span which this scope is managing.
-   */
-  public Span getSpan() {
-    return span;
-  }
-
-  /**
-   * Returns the span ID which this scope is managing.
-   */
-  public SpanId getSpanId() {
-    return span.getSpanId();
-  }
-
-  TraceScope getParent() {
-    return parent;
-  }
-
-  void setParent(TraceScope parent) {
-    this.parent = parent;
-  }
-
-  /**
-   * Detach this TraceScope from the current thread.
-   *
-   * It is OK to "leak" TraceScopes which have been detached.  They will not
-   * consume any resources other than a small amount of memory until they are
-   * garbage collected.  On the other hand, trace scopes which are still
-   * attached must never be leaked.
-   */
-  public void detach() {
-    if (detached) {
-      Tracer.throwClientError("Can't detach this TraceScope  because " +
-          "it is already detached.");
-    }
-    tracer.detachScope(this);
-    detached = true;
-    parent = null;
-  }
-
-  /**
-   * Attach this TraceScope to the current thread.
-   */
-  public void reattach() {
-    if (!detached) {
-      Tracer.throwClientError("Can't reattach this TraceScope  because " +
-          "it is not detached.");
-    }
-    tracer.reattachScope(this);
-    detached = false;
-  }
-
-  /**
-   * Close this TraceScope, ending the trace span it is managing.
-   */
-  @Override
-  public void close() {
-    tracer.closeScope(this);
-  }
-
-  public void addKVAnnotation(String key, String value) {
-    span.addKVAnnotation(key, value);
-  }
-
-  public void addTimelineAnnotation(String msg) {
-    span.addTimelineAnnotation(msg);
-  }
-
-  @Override
-  public String toString() {
-    return "TraceScope(tracerId=" + tracer.getTracerId() +
-        ", span=" + span.toJson() +
-        ", detached=" + detached + ")";
-  }
-}


[4/5] incubator-htrace git commit: HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)

Posted by cm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java b/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java
deleted file mode 100644
index 39d972d..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/Tracer.java
+++ /dev/null
@@ -1,673 +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.apache.htrace.core;
-
-import java.io.Closeable;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Use a Tracer instance inside a 'process' to collect and distribute its trace Spans.
- * Example processes are an HDFS DataNode or an HBase RegionServer. A Tracer instance is your
- * one-stop shop for all things tracing.
- * 
- * <p>
- */
-public class Tracer implements Closeable {
-  private static final Log LOG = LogFactory.getLog(Tracer.class);
-
-  public final static String SPAN_RECEIVER_CLASSES_KEY = "span.receiver.classes";
-  public final static String SAMPLER_CLASSES_KEY = "sampler.classes";
-
-  public static class Builder {
-    private String name;
-    private HTraceConfiguration conf = HTraceConfiguration.EMPTY;
-    private ClassLoader classLoader =
-        Builder.class.getClassLoader();
-    private TracerPool tracerPool = TracerPool.GLOBAL;
-
-    /**
-     * @deprecated Since 4.0.0. Use Constructor that takes a <code>name</code> argument instead
-     */
-    @Deprecated
-    public Builder() {
-    }
-
-    public Builder(final String name) {
-      name(name);
-    }
-
-    /**
-     * @param name
-     * @return This
-     * @deprecated Since 4.0.0. Use Constructor that takes a <code>name</code> argument instead.
-     */
-    @Deprecated
-    public Builder name(String name) {
-      this.name = name;
-      return this;
-    }
-
-    public Builder conf(HTraceConfiguration conf) {
-      this.conf = conf;
-      return this;
-    }
-
-    public Builder tracerPool(TracerPool tracerPool) {
-      this.tracerPool = tracerPool;
-      return this;
-    }
-
-    private void loadSamplers(List<Sampler> samplers) {
-      String classNamesStr = conf.get(SAMPLER_CLASSES_KEY, "");
-      List<String> classNames = getClassNamesFromConf(classNamesStr);
-      StringBuilder bld = new StringBuilder();
-      String prefix = "";
-      for (String className : classNames) {
-        try {
-          Sampler sampler = new Sampler.Builder(conf).
-            className(className).
-            classLoader(classLoader).
-            build();
-          samplers.add(sampler);
-          bld.append(prefix).append(className);
-          prefix = ", ";
-        } catch (Throwable e) {
-          LOG.error("Failed to create SpanReceiver of type " + className, e);
-        }
-      }
-      String resultString = bld.toString();
-      if (resultString.isEmpty()) {
-        resultString = "no samplers";
-      }
-      LOG.debug(SAMPLER_CLASSES_KEY + " = " + classNamesStr +
-          "; loaded " + resultString);
-    }
-
-    private void loadSpanReceivers() {
-      String classNamesStr = conf.get(SPAN_RECEIVER_CLASSES_KEY, "");
-      List<String> classNames = getClassNamesFromConf(classNamesStr);
-      StringBuilder bld = new StringBuilder();
-      String prefix = "";
-      for (String className : classNames) {
-        try {
-          tracerPool.loadReceiverType(className, conf, classLoader);
-          bld.append(prefix).append(className);
-          prefix = ", ";
-        } catch (Throwable e) {
-          LOG.error("Failed to create SpanReceiver of type " + className, e);
-        }
-      }
-      String resultString = bld.toString();
-      if (resultString.isEmpty()) {
-        resultString = "no span receivers";
-      }
-      LOG.debug(SPAN_RECEIVER_CLASSES_KEY + " = " + classNamesStr +
-          "; loaded " + resultString);
-    }
-
-    /**
-     * Get a list of class names from the HTrace configuration.
-     * Entries which are empty will be removed.  Entries which lack a package will
-     * be given the default package.
-     *
-     * @param classNamesStr     A semicolon-separated string containing a list
-     *                            of class names.
-     * @return                  A list of class names.
-     */
-    private List<String> getClassNamesFromConf(String classNamesStr) {
-      String classNames[] = classNamesStr.split(";");
-      LinkedList<String> cleanedClassNames = new LinkedList<String>();
-      for (String className : classNames) {
-        String cleanedClassName = className.trim();
-        if (!cleanedClassName.isEmpty()) {
-          cleanedClassNames.add(cleanedClassName);
-        }
-      }
-      return cleanedClassNames;
-    }
-
-    public Tracer build() {
-      if (name == null) {
-        throw new RuntimeException("You must specify a name for this Tracer.");
-      }
-      LinkedList<Sampler> samplers = new LinkedList<Sampler>();
-      loadSamplers(samplers);
-      String tracerId = new TracerId(conf, name).get();
-      Tracer tracer = new Tracer(tracerId, tracerPool,
-          samplers.toArray(new Sampler[samplers.size()]));
-      tracerPool.addTracer(tracer);
-      loadSpanReceivers();
-      if (LOG.isTraceEnabled()) {
-        LOG.trace("Created " + tracer + " for " + name);
-      }
-      return tracer;
-    }
-  }
-
-  /**
-   * The thread-specific context for this Tracer.
-   *
-   * This tracks the current number of trace scopes in a particular thread
-   * created by this tracer.  We use this to apply our samplers only for the
-   * "top-level" spans.
-   *
-   * Note that we can't put the TraceScope objects themselves in this context,
-   * since we need to be able to use TraceScopes created by other Tracers, and
-   * this context is per-Tracer.
-   */
-  private static class ThreadContext {
-    private long depth;
-
-    ThreadContext() {
-      this.depth = 0;
-    }
-
-    boolean isTopLevel() {
-      return (depth == 0);
-    }
-
-    void pushScope() {
-      depth++;
-    }
-
-    TraceScope pushNewScope(Tracer tracer, Span span, TraceScope parentScope) {
-      TraceScope scope = new TraceScope(tracer, span, parentScope);
-      threadLocalScope.set(scope);
-      depth++;
-      return scope;
-    }
-
-    void popScope() {
-      if (depth <= 0) {
-        throwClientError("There were more trace scopes closed than " +
-            "were opened.");
-      }
-      depth--;
-    }
-  };
-
-  /**
-   * A subclass of ThreadLocal that starts off with a non-null initial value in
-   * each thread.
-   */
-  private static class ThreadLocalContext extends ThreadLocal<ThreadContext> {
-    @Override
-    protected ThreadContext initialValue() {
-      return new ThreadContext();
-    }
-  };
-
-  /**
-   * The current trace scope.  This is global, so it is shared amongst all
-   * libraries using HTrace.
-   */
-  final static ThreadLocal<TraceScope> threadLocalScope =
-      new ThreadLocal<TraceScope>();
-
-  /**
-   * An empty array of SpanId objects.  Can be used rather than constructing a
-   * new object whenever we need an empty array.
-   */
-  private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0];
-
-  /**
-   * The tracerId.
-   */
-  private final String tracerId;
-
-  /**
-   * The TracerPool which this Tracer belongs to.
-   *
-   * This gets set to null after the Tracer is closed in order to catch some
-   * use-after-close errors.  Note that we do not synchronize access on this
-   * field, since it only changes when the Tracer is closed, and the Tracer
-   * should not be used after that.
-   */
-  private TracerPool tracerPool;
-
-  /**
-   * The current thread-local context for this particualr Tracer.
-   */
-  private final ThreadLocalContext threadContext;
-
-  /**
-   * The NullScope instance for this Tracer.
-   */
-  private final NullScope nullScope;
-
-  /**
-   * The currently active Samplers.
-   *
-   * Arrays are immutable once set.  You must take the Tracer lock in order to
-   * set this to a new array.  If this is null, the Tracer is closed.
-   */
-  private volatile Sampler[] curSamplers;
-
-  /**
-   * Log a client error, and throw an exception.
-   *
-   * @param str     The message to use in the log and the exception.
-   */
-  static void throwClientError(String str) {
-    LOG.error(str);
-    throw new RuntimeException(str);
-  }
-
-  /**
-   * @return If the current thread is tracing, this function returns the Tracer that is
-   * being used; otherwise, it returns null.
-   */
-  public static Tracer curThreadTracer() {
-    TraceScope traceScope = threadLocalScope.get();
-    if (traceScope == null) {
-      return null;
-    }
-    return traceScope.tracer;
-  }
-
-  Tracer(String tracerId, TracerPool tracerPool, Sampler[] curSamplers) {
-    this.tracerId = tracerId;
-    this.tracerPool = tracerPool;
-    this.threadContext = new ThreadLocalContext();
-    this.nullScope = new NullScope(this);
-    this.curSamplers = curSamplers;
-  }
-
-  public String getTracerId() {
-    return tracerId;
-  }
-
-  private TraceScope newScopeImpl(ThreadContext context, String description) {
-    Span span = new MilliSpan.Builder().
-        tracerId(tracerId).
-        begin(System.currentTimeMillis()).
-        description(description).
-        parents(EMPTY_PARENT_ARRAY).
-        spanId(SpanId.fromRandom()).
-        build();
-    return context.pushNewScope(this, span, null);
-  }
-
-  private TraceScope newScopeImpl(ThreadContext context, String description,
-        TraceScope parentScope) {
-    SpanId parentId = parentScope.getSpan().getSpanId();
-    Span span = new MilliSpan.Builder().
-        tracerId(tracerId).
-        begin(System.currentTimeMillis()).
-        description(description).
-        parents(new SpanId[] { parentId }).
-        spanId(parentId.newChildId()).
-        build();
-    return context.pushNewScope(this, span, parentScope);
-  }
-
-  private TraceScope newScopeImpl(ThreadContext context, String description,
-        SpanId parentId) {
-    Span span = new MilliSpan.Builder().
-        tracerId(tracerId).
-        begin(System.currentTimeMillis()).
-        description(description).
-        parents(new SpanId[] { parentId }).
-        spanId(parentId.newChildId()).
-        build();
-    return context.pushNewScope(this, span, null);
-  }
-
-  private TraceScope newScopeImpl(ThreadContext context, String description,
-        TraceScope parentScope, SpanId secondParentId) {
-    SpanId parentId = parentScope.getSpan().getSpanId();
-    Span span = new MilliSpan.Builder().
-        tracerId(tracerId).
-        begin(System.currentTimeMillis()).
-        description(description).
-        parents(new SpanId[] { parentId, secondParentId }).
-        spanId(parentId.newChildId()).
-        build();
-    return context.pushNewScope(this, span, parentScope);
-  }
-
-  /**
-   * Create a new trace scope.
-   *
-   * If there are no scopes above the current scope, we will apply our
-   * configured samplers. Otherwise, we will create a trace Span only if this thread
-   * is already tracing, or if the passed parentID was valid.
-   *
-   * @param description         The description of the new span to create.
-   * @param parentId            If this is a valid span ID, it will be added to
-   *                              the parents of the new span we create.
-   * @return                    The new trace scope.
-   */
-  public TraceScope newScope(String description, SpanId parentId) {
-    TraceScope parentScope = threadLocalScope.get();
-    ThreadContext context = threadContext.get();
-    if (parentScope != null) {
-      if (parentId.isValid() &&
-          (!parentId.equals(parentScope.getSpan().getSpanId()))) {
-        return newScopeImpl(context, description, parentScope, parentId);
-      } else {
-        return newScopeImpl(context, description, parentScope);
-      }
-    } else if (parentId.isValid()) {
-      return newScopeImpl(context, description, parentId);
-    }
-    if (!context.isTopLevel()) {
-      context.pushScope();
-      return nullScope;
-    }
-    if (!sample()) {
-      context.pushScope();
-      return nullScope;
-    }
-    return newScopeImpl(context, description);
-  }
-
-  /**
-   * Create a new trace scope.
-   *
-   * If there are no scopes above the current scope, we will apply our
-   * configured samplers. Otherwise, we will create a trace Span only if this thread
-   * is already tracing.
-   * @param description         The description of the new span to create.
-   * @return                    The new trace scope.
-   */
-  public TraceScope newScope(String description) {
-    TraceScope parentScope = threadLocalScope.get();
-    ThreadContext context = threadContext.get();
-    if (parentScope != null) {
-      return newScopeImpl(context, description, parentScope);
-    }
-    if (!context.isTopLevel()) {
-      context.pushScope();
-      return nullScope;
-    }
-    if (!sample()) {
-      context.pushScope();
-      return nullScope;
-    }
-    return newScopeImpl(context, description);
-  }
-
-  /**
-   * Return a null trace scope.
-   */
-  public TraceScope newNullScope() {
-    ThreadContext context = threadContext.get();
-    context.pushScope();
-    return nullScope;
-  }
-
-  /**
-   * Wrap the callable in a TraceCallable, if tracing.
-   *
-   * @return The callable provided, wrapped if tracing, 'callable' if not.
-   */
-  public <V> Callable<V> wrap(Callable<V> callable, String description) {
-    TraceScope parentScope = threadLocalScope.get();
-    if (parentScope == null) {
-      return callable;
-    }
-    return new TraceCallable<V>(this, parentScope, callable, description);
-  }
-
-  /**
-   * Wrap the runnable in a TraceRunnable, if tracing
-   *
-   * @return The runnable provided, wrapped if tracing, 'runnable' if not.
-   */
-  public Runnable wrap(Runnable runnable, String description) {
-    TraceScope parentScope = threadLocalScope.get();
-    if (parentScope == null) {
-      return runnable;
-    }
-    return new TraceRunnable(this, parentScope, runnable, description);
-  }
-
-  public TraceExecutorService newTraceExecutorService(ExecutorService impl,
-                                                      String scopeName) {
-    return new TraceExecutorService(this, scopeName, impl);
-  }
-
-  public TracerPool getTracerPool() {
-    if (tracerPool == null) {
-      throwClientError(toString() + " is closed.");
-    }
-    return tracerPool;
-  }
-
-  /**
-   * Returns an object that will trace all calls to itself.
-   */
-  @SuppressWarnings("unchecked")
-  <T, V> T createProxy(final T instance) {
-    final Tracer tracer = this;
-    InvocationHandler handler = new InvocationHandler() {
-      @Override
-      public Object invoke(Object obj, Method method, Object[] args)
-          throws Throwable {
-        TraceScope scope = tracer.newScope(method.getName());
-        try {
-          return method.invoke(instance, args);
-        } catch (Throwable ex) {
-          ex.printStackTrace();
-          throw ex;
-        } finally {
-          scope.close();
-        }
-      }
-    };
-    return (T) Proxy.newProxyInstance(instance.getClass().getClassLoader(),
-        instance.getClass().getInterfaces(), handler);
-  }
-
-  /**
-   * Return true if we should create a new top-level span.
-   *
-   * We will create the span if any configured sampler returns true.
-   */
-  private boolean sample() {
-    Sampler[] samplers = curSamplers;
-    for (Sampler sampler : samplers) {
-      if (sampler.next()) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /**
-   * Returns an array of all the current Samplers.
-   *
-   * Note that if the current Samplers change, those changes will not be
-   * reflected in this array.  In other words, this array may be stale.
-   */
-  public Sampler[] getSamplers() {
-    return curSamplers;
-  }
-
-  /**
-   * Add a new Sampler.
-   *
-   * @param sampler       The new sampler to add.
-   *                      You cannot add a particular Sampler object more
-   *                        than once.  You may add multiple Sampler objects
-   *                        of the same type, although this is not recommended.
-   *
-   * @return              True if the sampler was added; false if it already had
-   *                        been added earlier.
-   */
-  public synchronized boolean addSampler(Sampler sampler) {
-    if (tracerPool == null) {
-      throwClientError(toString() + " is closed.");
-    }
-    Sampler[] samplers = curSamplers;
-    for (int i = 0; i < samplers.length; i++) {
-      if (samplers[i] == sampler) {
-        return false;
-      }
-    }
-    Sampler[] newSamplers =
-        Arrays.copyOf(samplers, samplers.length + 1);
-    newSamplers[samplers.length] = sampler;
-    curSamplers = newSamplers;
-    return true;
-  }
-
-  /**
-   * Remove a SpanReceiver.
-   *
-   * @param sampler       The sampler to remove.
-   */
-  public synchronized boolean removeSampler(Sampler sampler) {
-    if (tracerPool == null) {
-      throwClientError(toString() + " is closed.");
-    }
-    Sampler[] samplers = curSamplers;
-    for (int i = 0; i < samplers.length; i++) {
-      if (samplers[i] == sampler) {
-        Sampler[] newSamplers = new Sampler[samplers.length - 1];
-        System.arraycopy(samplers, 0, newSamplers, 0, i);
-        System.arraycopy(samplers, i + 1, newSamplers, i,
-            samplers.length - i - 1);
-        curSamplers = newSamplers;
-        return true;
-      }
-    }
-    return false;
-  }
-
-  void detachScope(TraceScope scope) {
-    TraceScope curScope = threadLocalScope.get();
-    if (curScope != scope) {
-      throwClientError("Can't detach TraceScope for " +
-          scope.getSpan().toJson() + " because it is not the current " +
-          "TraceScope in thread " + Thread.currentThread().getName());
-    }
-    ThreadContext context = threadContext.get();
-    context.popScope();
-    threadLocalScope.set(scope.getParent());
-  }
-
-  void reattachScope(TraceScope scope) {
-    TraceScope parent = threadLocalScope.get();
-    Tracer.threadLocalScope.set(scope);
-    ThreadContext context = threadContext.get();
-    context.pushScope();
-    scope.setParent(parent);
-  }
-
-  void closeScope(TraceScope scope) {
-    TraceScope curScope = threadLocalScope.get();
-    if (curScope != scope) {
-      throwClientError("Can't close TraceScope for " +
-          scope.getSpan().toJson() + " because it is not the current " +
-          "TraceScope in thread " + Thread.currentThread().getName());
-    }
-    if (tracerPool == null) {
-      throwClientError(toString() + " is closed.");
-    }
-    SpanReceiver[] receivers = tracerPool.getReceivers();
-    if (receivers == null) {
-      throwClientError(toString() + " is closed.");
-    }
-    ThreadContext context = threadContext.get();
-    context.popScope();
-    threadLocalScope.set(scope.getParent());
-    scope.setParent(null);
-    Span span = scope.getSpan();
-    span.stop();
-    for (SpanReceiver receiver : receivers) {
-      receiver.receiveSpan(span);
-    }
-  }
-
-  void popNullScope() {
-    TraceScope curScope = threadLocalScope.get();
-    if (curScope != null) {
-      throwClientError("Attempted to close an empty scope, but it was not " +
-          "the current thread scope in thread " +
-          Thread.currentThread().getName());
-    }
-    ThreadContext context = threadContext.get();
-    context.popScope();
-  }
-
-  public static Span getCurrentSpan() {
-    TraceScope curScope = threadLocalScope.get();
-    if (curScope == null) {
-      return null;
-    } else {
-      return curScope.getSpan();
-    }
-  }
-
-  public static SpanId getCurrentSpanId() {
-    TraceScope curScope = threadLocalScope.get();
-    if (curScope == null) {
-      return SpanId.INVALID;
-    } else {
-      return curScope.getSpan().getSpanId();
-    }
-  }
-
-  @Override
-  public synchronized void close() {
-    if (tracerPool == null) {
-      return;
-    }
-    curSamplers = new Sampler[0];
-    tracerPool.removeTracer(this);
-  }
-
-  /**
-   * Get the hash code of a Tracer object.
-   *
-   * This hash code is based on object identity.
-   * This is used in TracerPool to create a hash table of Tracers.
-   */
-  @Override
-  public int hashCode() {
-    return System.identityHashCode(this);
-  }
-
-  /**
-   * Compare two tracer objects.
-   *
-   * Tracer objects are always compared by object equality.
-   * This is used in TracerPool to create a hash table of Tracers.
-   */
-  @Override
-  public boolean equals(Object other) {
-    return (this == other);
-  }
-
-  @Override
-  public String toString() {
-    return "Tracer(" + tracerId + ")";
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java b/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java
deleted file mode 100644
index da482fe..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TracerId.java
+++ /dev/null
@@ -1,294 +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.apache.htrace.core;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.lang.management.ManagementFactory;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.util.Enumeration;
-import java.util.Locale;
-import java.util.TreeSet;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * The HTrace tracer ID.<p/>
- *
- * HTrace tracer IDs are created from format strings.
- * Format strings contain variables which the TracerId class will
- * replace with the correct values at runtime.<p/>
- *
- * <ul>
- * <li>%{tname}: the tracer name supplied when creating the Tracer.</li>
- * <li>%{pname}: the process name obtained from the JVM.</li>
- * <li>%{ip}: will be replaced with an ip address.</li>
- * <li>%{pid}: the numerical process ID from the operating system.</li>
- * </ul><p/>
- *
- * For example, the string "%{pname}/%{ip}" will be replaced with something
- * like: DataNode/192.168.0.1, assuming that the process' name is DataNode
- * and its IP address is 192.168.0.1.<p/>
- *
- *  ID strings can contain backslashes as escapes.
- * For example, "\a" will map to "a".  "\%{ip}" will map to the literal
- * string "%{ip}", not the IP address.  A backslash itself can be escaped by a
- * preceding backslash.
- */
-public final class TracerId {
-  private static final Log LOG = LogFactory.getLog(TracerId.class);
-
-  /**
-   * The configuration key to use for process id
-   */
-  public static final String TRACER_ID_KEY = "tracer.id";
-
-  /**
-   * The default tracer ID to use if no other ID is configured.
-   */
-  private static final String DEFAULT_TRACER_ID = "%{tname}/%{ip}";
-
-  private final String tracerName;
-
-  private final String tracerId;
-
-  public TracerId(HTraceConfiguration conf, String tracerName) {
-    this.tracerName = tracerName;
-    String fmt = conf.get(TRACER_ID_KEY, DEFAULT_TRACER_ID);
-    StringBuilder bld = new StringBuilder();
-    StringBuilder varBld = null;
-    boolean escaping = false;
-    int varSeen = 0;
-    for (int i = 0, len = fmt.length() ; i < len; i++) {
-      char c = fmt.charAt(i);
-      if (c == '\\') {
-        if (!escaping) {
-          escaping = true;
-          continue;
-        }
-      }
-      switch (varSeen) {
-        case 0:
-          if (c == '%') {
-            if (!escaping) {
-              varSeen = 1;
-              continue;
-            }
-          }
-          escaping = false;
-          varSeen = 0;
-          bld.append(c);
-          break;
-        case 1:
-          if (c == '{') {
-            if (!escaping) {
-              varSeen = 2;
-              varBld = new StringBuilder();
-              continue;
-            }
-          }
-          escaping = false;
-          varSeen = 0;
-          bld.append("%").append(c);
-          break;
-        default:
-          if (c == '}') {
-            if (!escaping) {
-              String var = varBld.toString();
-              bld.append(processShellVar(var));
-              varBld = null;
-              varSeen = 0;
-              continue;
-            }
-          }
-          escaping = false;
-          varBld.append(c);
-          varSeen++;
-          break;
-      }
-    }
-    if (varSeen > 0) {
-      LOG.warn("Unterminated process ID substitution variable at the end " +
-          "of format string " + fmt);
-    }
-    this.tracerId = bld.toString();
-    if (LOG.isTraceEnabled()) {
-      LOG.trace("ProcessID(fmt=" + fmt + "): computed process ID of \"" +
-          this.tracerId + "\"");
-    }
-  }
-
-  private String processShellVar(String var) {
-    if (var.equals("tname")) {
-      return tracerName;
-    } else if (var.equals("pname")) {
-      return getProcessName();
-    } else if (var.equals("ip")) {
-      return getBestIpString();
-    } else if (var.equals("pid")) {
-      return Long.valueOf(getOsPid()).toString();
-    } else {
-      LOG.warn("unknown ProcessID variable " + var);
-      return "";
-    }
-  }
-
-  static String getProcessName() {
-    String cmdLine = System.getProperty("sun.java.command");
-    if (cmdLine != null && !cmdLine.isEmpty()) {
-      String fullClassName = cmdLine.split("\\s+")[0];
-      String[] classParts = fullClassName.split("\\.");
-      cmdLine = classParts[classParts.length - 1];
-    }
-    return (cmdLine == null || cmdLine.isEmpty()) ? "Unknown" : cmdLine;
-  }
-
-  /**
-   * Get the best IP address that represents this node.<p/>
-   *
-   * This is complicated since nodes can have multiple network interfaces,
-   * and each network interface can have multiple IP addresses.  What we're
-   * looking for here is an IP address that will serve to identify this node
-   * to HTrace.  So we prefer site-local addresess (i.e. private ones on the
-   * LAN) to publicly routable interfaces.  If there are multiple addresses
-   * to choose from, we select the one which comes first in textual sort
-   * order.  This should ensure that we at least consistently call each node
-   * by a single name.
-   */
-  static String getBestIpString() {
-    Enumeration<NetworkInterface> ifaces;
-    try {
-      ifaces = NetworkInterface.getNetworkInterfaces();
-    } catch (SocketException e) {
-      LOG.error("Error getting network interfaces", e);
-      return "127.0.0.1";
-    }
-    TreeSet<String> siteLocalCandidates = new TreeSet<String>();
-    TreeSet<String> candidates = new TreeSet<String>();
-    while (ifaces.hasMoreElements()) {
-      NetworkInterface iface = ifaces.nextElement();
-      for (Enumeration<InetAddress> addrs =
-               iface.getInetAddresses(); addrs.hasMoreElements();) {
-        InetAddress addr = addrs.nextElement();
-        if (!addr.isLoopbackAddress()) {
-          if (addr.isSiteLocalAddress()) {
-            siteLocalCandidates.add(addr.getHostAddress());
-          } else {
-            candidates.add(addr.getHostAddress());
-          }
-        }
-      }
-    }
-    if (!siteLocalCandidates.isEmpty()) {
-      return siteLocalCandidates.first();
-    }
-    if (!candidates.isEmpty()) {
-      return candidates.first();
-    }
-    return "127.0.0.1";
-  }
-
-  /**
-   * Get the process id from the operating system.<p/>
-   *
-   * Unfortunately, there is no simple method to get the process id in Java.
-   * The approach we take here is to use the shell method (see
-   * {TracerId#getOsPidFromShellPpid}) unless we are on Windows, where the
-   * shell is not available.  On Windows, we use
-   * {TracerId#getOsPidFromManagementFactory}, which depends on some
-   * undocumented features of the JVM, but which doesn't require a shell.
-   */
-  static long getOsPid() {
-    if ((System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)).
-        contains("windows")) {
-      return getOsPidFromManagementFactory();
-    } else {
-      return getOsPidFromShellPpid();
-    }
-  }
-
-  /**
-   * Get the process ID by executing a shell and printing the PPID (parent
-   * process ID).<p/>
-   *
-   * This method of getting the process ID doesn't depend on any undocumented
-   * features of the virtual machine, and should work on almost any UNIX
-   * operating system.
-   */
-  private static long getOsPidFromShellPpid() {
-    Process p = null;
-    StringBuilder sb = new StringBuilder();
-    try {
-      p = new ProcessBuilder("/usr/bin/env", "sh", "-c", "echo $PPID").
-        redirectErrorStream(true).start();
-      BufferedReader reader = new BufferedReader(
-          new InputStreamReader(p.getInputStream()));
-      String line = "";
-      while ((line = reader.readLine()) != null) {
-        sb.append(line.trim());
-      }
-      int exitVal = p.waitFor();
-      if (exitVal != 0) {
-        throw new IOException("Process exited with error code " +
-            Integer.valueOf(exitVal).toString());
-      }
-    } catch (InterruptedException e) {
-      LOG.error("Interrupted while getting operating system pid from " +
-          "the shell.", e);
-      return 0L;
-    } catch (IOException e) {
-      LOG.error("Error getting operating system pid from the shell.", e);
-      return 0L;
-    } finally {
-      if (p != null) {
-        p.destroy();
-      }
-    }
-    try {
-      return Long.parseLong(sb.toString());
-    } catch (NumberFormatException e) {
-      LOG.error("Error parsing operating system pid from the shell.", e);
-      return 0L;
-    }
-  }
-
-  /**
-   * Get the process ID by looking at the name of the managed bean for the
-   * runtime system of the Java virtual machine.<p/>
-   *
-   * Although this is undocumented, in the Oracle JVM this name is of the form
-   * [OS_PROCESS_ID]@[HOSTNAME].
-   */
-  private static long getOsPidFromManagementFactory() {
-    try {
-      return Long.parseLong(ManagementFactory.getRuntimeMXBean().
-          getName().split("@")[0]);
-    } catch (NumberFormatException e) {
-      LOG.error("Failed to get the operating system process ID from the name " +
-          "of the managed bean for the JVM.", e);
-      return 0L;
-    }
-  }
-
-  public String get() {
-    return tracerId;
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java b/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java
deleted file mode 100644
index 26e39f5..0000000
--- a/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java
+++ /dev/null
@@ -1,285 +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.apache.htrace.core;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A pool of Tracer objects.
- *
- * There may be more than one {@link Tracer} running inside a single 'process'; for example,
- * unit tests may spin up a DataNode, a NameNode, and HDFS clients all running in a single JVM
- * instance, each with its own Tracer. TracerPool is where all Tracer instances register
- * on creation so Tracers can coordinate around shared resources such as {@link SpanReceiver}
- * instances. TracerPool takes care of properly cleaning up registered Tracer instances on shutdown.
- */
-public class TracerPool {
-  private static final Log LOG = LogFactory.getLog(TracerPool.class);
-
-  /**
-   * The global pool of tracer objects.
-   *
-   * This is the pool that new tracers get put into by default.
-   */
-  static final TracerPool GLOBAL = new TracerPool("Global");
-
-  /**
-   * The shutdown hook which closes the Tracers in this pool when the process is
-   * shutting down.
-   */
-  private class SpanReceiverShutdownHook extends Thread {
-    SpanReceiverShutdownHook() {
-      setName("SpanReceiverShutdownHook");
-      setDaemon(false);
-    }
-
-    @Override
-    public void run() {
-      removeAndCloseAllSpanReceivers();
-    }
-  }
-
-  /**
-   * The name of this TracerPool.
-   */
-  private final String name;
-
-  /**
-   * The current span receivers which these tracers are using.
-   *
-   * Can be read locklessly.  Must be written under the lock.
-   * The array itself should never be modified.
-   */
-  private volatile SpanReceiver[] curReceivers;
-
-  /**
-   * The currently installed shutdown hook, or null if no hook has been
-   * installed.
-   */
-  private SpanReceiverShutdownHook shutdownHook;
-
-  /**
-   * The current Tracers.
-   */
-  private final HashSet<Tracer> curTracers;
-
-  /**
-   * Get the global tracer pool.
-   */
-  public static TracerPool getGlobalTracerPool() {
-    return GLOBAL;
-  }
-
-  public TracerPool(String name) {
-    this.name = name;
-    this.shutdownHook = null;
-    this.curTracers = new HashSet<Tracer>();
-    this.curReceivers = new SpanReceiver[0];
-  }
-
-  /**
-   * Return the name of this TracerPool.
-   */
-  public String getName() {
-    return name;
-  }
-
-  /**
-   * Returns an array of all the current span receivers.
-   *
-   * Note that if the current span receivers change, those changes will not be
-   * reflected in this array.  In other words, this array may be stale.
-   */
-  public SpanReceiver[] getReceivers() {
-    return curReceivers;
-  }
-
-  /**
-   * Add a new span receiver.
-   *
-   * @param receiver        The new receiver to add.
-   *
-   * @return                True if the new receiver was added; false if it
-   *                          already was there.
-   */
-  public synchronized boolean addReceiver(SpanReceiver receiver) {
-    SpanReceiver[] receivers = curReceivers;
-    for (int i = 0; i < receivers.length; i++) {
-      if (receivers[i] == receiver) {
-        LOG.trace(toString() + ": can't add receiver " + receiver.toString() +
-                  " since it is already in this pool.");
-        return false;
-      }
-    }
-    SpanReceiver[] newReceivers =
-        Arrays.copyOf(receivers, receivers.length + 1);
-    newReceivers[receivers.length] = receiver;
-    registerShutdownHookIfNeeded();
-    curReceivers = newReceivers;
-    LOG.trace(toString() + ": added receiver " + receiver.toString());
-    return true;
-  }
-
-  /**
-   * Register the shutdown hook if needed.
-   */
-  private synchronized void registerShutdownHookIfNeeded() {
-    if (shutdownHook != null) {
-      return;
-    }
-    shutdownHook = new SpanReceiverShutdownHook();
-    Runtime.getRuntime().addShutdownHook(shutdownHook);
-    LOG.trace(toString() + ": registered shutdown hook.");
-  }
-
-  /**
-   * Remove a span receiver.
-   *
-   * @param receiver        The receiver to remove.
-   *
-   * @return                True if the receiver was removed; false if it
-   *                          did not exist in this pool.
-   */
-  public synchronized boolean removeReceiver(SpanReceiver receiver) {
-    SpanReceiver[] receivers = curReceivers;
-    for (int i = 0; i < receivers.length; i++) {
-      if (receivers[i] == receiver) {
-        SpanReceiver[] newReceivers = new SpanReceiver[receivers.length - 1];
-        System.arraycopy(receivers, 0, newReceivers, 0, i);
-        System.arraycopy(receivers, i + 1, newReceivers, i,
-            receivers.length - i - 1);
-        curReceivers = newReceivers;
-        LOG.trace(toString() + ": removed receiver " + receiver.toString());
-        return true;
-      }
-    }
-    LOG.trace(toString() + ": can't remove receiver " + receiver.toString() +
-        " since it's not currently in this pool.");
-    return false;
-  }
-
-  /**
-   * Remove and close a span receiver.
-   *
-   * @param receiver        The receiver to remove.
-   *
-   * @return                True if the receiver was removed; false if it
-   *                          did not exist in this pool.
-   */
-  public boolean removeAndCloseReceiver(SpanReceiver receiver) {
-    if (!removeReceiver(receiver)) {
-      return false;
-    }
-    try {
-      LOG.trace(toString() + ": closing receiver " + receiver.toString());
-      receiver.close();
-    } catch (Throwable t) {
-      LOG.error(toString() + ": error closing " + receiver.toString(), t);
-    }
-    return true;
-  }
-
-  /**
-   * Remove and close all of the span receivers.
-   */
-  private synchronized void removeAndCloseAllSpanReceivers() {
-    SpanReceiver[] receivers = curReceivers;
-    curReceivers = new SpanReceiver[0];
-    for (SpanReceiver receiver : receivers) {
-      try {
-        LOG.trace(toString() + ": closing receiver " + receiver.toString());
-        receiver.close();
-      } catch (Throwable t) {
-        LOG.error(toString() + ": error closing " + receiver.toString(), t);
-      }
-    }
-  }
-
-  /**
-   * Given a SpanReceiver class name, return the existing instance of that span
-   * receiver, if possible; otherwise, invoke the callable to create a new
-   * instance.
-   *
-   * @param className       The span receiver class name.
-   * @param conf            The HTrace configuration.
-   * @param classLoader     The class loader to use.
-   *
-   * @return                The SpanReceiver.
-   */
-  public synchronized SpanReceiver loadReceiverType(String className,
-      HTraceConfiguration conf, ClassLoader classLoader) {
-    SpanReceiver[] receivers = curReceivers;
-    for (SpanReceiver receiver : receivers) {
-      if (receiver.getClass().getName().equals(className)) {
-        LOG.trace(toString() + ": returning a reference to receiver " +
-                  receiver.toString());
-        return receiver;
-      }
-    }
-    LOG.trace(toString() + ": creating a new SpanReceiver of type " +
-              className);
-    SpanReceiver receiver = new SpanReceiver.Builder(conf).
-        className(className).
-        classLoader(classLoader).
-        build();
-    addReceiver(receiver);
-    return receiver;
-  }
-
-  /**
-   * Returns an array of all the current Tracers.
-   *
-   * Note that if the current Tracers change, those changes will not be
-   * reflected in this array.  In other words, this array may be stale.
-   */
-  public synchronized Tracer[] getTracers() {
-    return curTracers.toArray(new Tracer[curTracers.size()]);
-  }
-
-  /**
-   * Add a new Tracer.
-   */
-  synchronized void addTracer(Tracer tracer) {
-    if (curTracers.add(tracer)) {
-      LOG.trace(toString() + ": adding tracer " + tracer.toString());
-    }
-  }
-
-  /**
-   * Remove a Tracer.
-   *
-   * If the Tracer removed was the last one, we will close all the SpanReceiver
-   * objects that we're managing.
-   */
-  synchronized void removeTracer(Tracer tracer) {
-    if (curTracers.remove(tracer)) {
-      LOG.trace(toString() + ": removing tracer " + tracer.toString());
-      if (curTracers.size() == 0) {
-        removeAndCloseAllSpanReceivers();
-      }
-    }
-  }
-
-  @Override
-  public String toString() {
-    return "TracerPool(" + name + ")";
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java b/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java
deleted file mode 100644
index 87ae8e9..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java
+++ /dev/null
@@ -1,205 +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.apache.htrace.core;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.hamcrest.CoreMatchers.containsString;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestBadClient {
-  @After
-  public void clearBadState() {
-    // Clear the bad trace state so that we don't disrupt other unit tests
-    // that run in this JVM.
-    Tracer.threadLocalScope.set(null);
-  }
-
-  /**
-   * Test closing an outer scope when an inner one is still active.
-   */
-  @Test
-  public void TestClosingOuterScope() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestClosingOuterScopeTracer").
-        tracerPool(new TracerPool("TestClosingOuterScope")).
-        conf(HTraceConfiguration.
-            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
-    boolean gotException = false;
-    TraceScope outerScope = tracer.newScope("outer");
-    TraceScope innerScope = tracer.newScope("inner");
-    try {
-      outerScope.close();
-    } catch (RuntimeException e) {
-      assertThat(e.getMessage(),
-          containsString("it is not the current TraceScope"));
-      gotException = true;
-    }
-    assertTrue("Expected to get exception because of improper " +
-        "scope closure.", gotException);
-    innerScope.close();
-    tracer.close();
-  }
-
-  /**
-   * Test calling detach() two times on a scope object.
-   */
-  @Test
-  public void TestDoubleDetachIsCaught() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestDoubleDetach").
-        tracerPool(new TracerPool("TestDoubleDetachIsCaught")).
-        conf(HTraceConfiguration.
-            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
-    boolean gotException = false;
-    TraceScope myScope = tracer.newScope("myScope");
-    myScope.detach();
-    try {
-      myScope.detach();
-    } catch (RuntimeException e) {
-      assertThat(e.getMessage(),
-          containsString("it is already detached."));
-      gotException = true;
-    }
-    assertTrue("Expected to get exception because of double TraceScope " +
-        "detach.", gotException);
-    tracer.close();
-  }
-
-  /**
-   * Test calling detach() two times on a scope object.
-   */
-  @Test
-  public void TestDoubleDetachOnNullScope() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestDoubleDetachOnNullScope").
-        tracerPool(new TracerPool("TestDoubleDetachOnNullScope")).
-        conf(HTraceConfiguration.
-            fromKeyValuePairs("sampler.classes", "NeverSampler")).build();
-    boolean gotException = false;
-    TraceScope myScope = tracer.newScope("myScope");
-    myScope.detach();
-    try {
-      myScope.detach();
-    } catch (RuntimeException e) {
-      assertThat(e.getMessage(),
-          containsString("it is already detached."));
-      gotException = true;
-    }
-    assertTrue("Expected to get exception because of double TraceScope " +
-        "detach on NullScope.", gotException);
-    tracer.close();
-  }
-
-  /**
-   * Test calling reattach() two times on a scope object.
-   */
-  @Test
-  public void TestDoubleReattachIsCaught() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestDoubleReattach").
-        tracerPool(new TracerPool("TestDoubleReattachIsCaught")).
-        conf(HTraceConfiguration.
-            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
-    boolean gotException = false;
-    TraceScope myScope = tracer.newScope("myScope");
-    myScope.detach();
-    myScope.reattach();
-    try {
-      myScope.reattach();
-    } catch (RuntimeException e) {
-      assertThat(e.getMessage(),
-          containsString("it is not detached."));
-      gotException = true;
-    }
-    assertTrue("Expected to get exception because of double TraceScope " +
-        "reattach.", gotException);
-    tracer.close();
-  }
-
-  private static class ScopeHolder {
-    TraceScope scope;
-
-    void set(TraceScope scope) {
-      this.scope = scope;
-    }
-  }
-
-  /**
-   * Test correctly passing spans between threads using detach().
-   */
-  @Test
-  public void TestPassingSpanBetweenThreads() throws Exception {
-    final Tracer tracer = new Tracer.Builder().
-        name("TestPassingSpanBetweenThreads").
-        tracerPool(new TracerPool("TestPassingSpanBetweenThreads")).
-        conf(HTraceConfiguration.
-            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
-    POJOSpanReceiver receiver =
-        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
-    tracer.getTracerPool().addReceiver(receiver);
-    final ScopeHolder scopeHolder = new ScopeHolder();
-    Thread th = new Thread(new Runnable() {
-      @Override
-      public void run() {
-        TraceScope workerScope = tracer.newScope("workerSpan");
-        workerScope.detach();
-        scopeHolder.set(workerScope);
-      }
-    });
-    th.start();
-    th.join();
-    TraceScope workerScope = scopeHolder.scope;
-    SpanId workerScopeId = workerScope.getSpan().getSpanId();
-
-    // Create new scope whose parent is the worker thread's span.
-    workerScope.reattach();
-    TraceScope nested = tracer.newScope("nested");
-    nested.close();
-    // Create another span which also descends from the worker thread's span.
-    TraceScope nested2 = tracer.newScope("nested2");
-    nested2.close();
-
-    // Close the worker thread's span.
-    workerScope.close();
-
-    // We can create another descendant, even though the worker thread's span
-    // has been stopped.
-    TraceScope lateChildScope = tracer.newScope("lateChild", workerScopeId);
-    lateChildScope.close();
-    tracer.close();
-
-    TraceGraph traceGraph = new TraceGraph(receiver.getSpans());
-    Collection<Span> rootSpans =
-        traceGraph.getSpansByParent().find(SpanId.INVALID);
-    Assert.assertEquals(1, rootSpans.size());
-    Assert.assertEquals(workerScopeId,
-        rootSpans.iterator().next().getSpanId());
-    Collection<Span> childSpans =
-        traceGraph.getSpansByParent().find(workerScopeId);
-    Assert.assertEquals(3, childSpans.size());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java b/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java
deleted file mode 100644
index e26115d..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestCountSampler.java
+++ /dev/null
@@ -1,41 +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.apache.htrace.core;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestCountSampler {
-
-  @Test
-  public void testNext() {
-    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++) {
-      if (half.next())
-        halfCount++;
-      if (hundred.next())
-        hundredCount++;
-    }
-    Assert.assertEquals(2, hundredCount);
-    Assert.assertEquals(100, halfCount);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java b/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java
deleted file mode 100644
index 06ca189..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java
+++ /dev/null
@@ -1,130 +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.apache.htrace.core;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.htrace.core.TraceGraph.SpansByParent;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestHTrace {
-  @Test
-  public void TestTracerCreateAndClose() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestSimpleScope").
-        tracerPool(new TracerPool("TestTracerCreateAndClose")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "sampler.classes", "AlwaysSampler")).
-        build();
-    POJOSpanReceiver receiver =
-        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
-    tracer.getTracerPool().addReceiver(receiver);
-    tracer.close();
-    Assert.assertTrue(receiver.getSpans().isEmpty());
-  }
-
-  @Test
-  public void TestSimpleScope() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestSimpleScope").
-        tracerPool(new TracerPool("TestSimpleScope")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "sampler.classes", "AlwaysSampler")).
-        build();
-    POJOSpanReceiver receiver =
-        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
-    tracer.getTracerPool().addReceiver(receiver);
-    TraceScope scope = tracer.newScope("Foo");
-    scope.close();
-    tracer.close();
-    Assert.assertEquals(1, receiver.getSpans().size());
-    Span span = receiver.getSpans().iterator().next();
-    Assert.assertEquals(0, span.getParents().length);
-  }
-
-  @Test
-  public void TestCreateSpans() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("TestCreateSpans").
-        tracerPool(new TracerPool("TestCreateSpans")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "sampler.classes", "AlwaysSampler")).
-        build();
-    POJOSpanReceiver receiver =
-        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
-    tracer.getTracerPool().addReceiver(receiver);
-    TraceCreator traceCreator = new TraceCreator(tracer);
-    traceCreator.createSampleRpcTrace();
-    traceCreator.createSimpleTrace();
-    traceCreator.createThreadedTrace();
-    tracer.close();
-    TraceGraph traceGraph = new TraceGraph(receiver.getSpans());
-    Collection<Span> roots = traceGraph.getSpansByParent().find(SpanId.INVALID);
-    Assert.assertTrue("Trace tree must have roots", !roots.isEmpty());
-    Assert.assertEquals(3, roots.size());
-
-    Map<String, Span> descriptionToRootSpan = new HashMap<String, Span>();
-    for (Span root : roots) {
-      descriptionToRootSpan.put(root.getDescription(), root);
-    }
-
-    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
-        TraceCreator.RPC_TRACE_ROOT));
-    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
-        TraceCreator.SIMPLE_TRACE_ROOT));
-    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
-        TraceCreator.THREADED_TRACE_ROOT));
-
-    SpansByParent spansByParentId = traceGraph.getSpansByParent();
-
-    Span rpcTraceRoot = descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT);
-    Assert.assertEquals(1, spansByParentId.find(rpcTraceRoot.getSpanId()).size());
-
-    Span rpcTraceChild1 = spansByParentId.find(rpcTraceRoot.getSpanId())
-        .iterator().next();
-    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild1.getSpanId()).size());
-
-    Span rpcTraceChild2 = spansByParentId.find(rpcTraceChild1.getSpanId())
-        .iterator().next();
-    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild2.getSpanId()).size());
-
-    Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId())
-        .iterator().next();
-    Assert.assertEquals(0, spansByParentId.find(rpcTraceChild3.getSpanId()).size());
-  }
-
-  @Test(timeout=60000)
-  public void testRootSpansHaveNonZeroSpanId() throws Exception {
-    Tracer tracer = new Tracer.Builder().
-        name("testRootSpansHaveNonZeroSpanId").
-        tracerPool(new TracerPool("testRootSpansHaveNonZeroSpanId")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "sampler.classes", "AlwaysSampler")).build();
-    TraceScope scope = tracer.
-        newScope("myRootSpan", new SpanId(100L, 200L));
-    Assert.assertNotNull(scope);
-    Assert.assertEquals("myRootSpan", scope.getSpan().getDescription());
-    Assert.assertTrue(scope.getSpan().getSpanId().isValid());
-    Assert.assertEquals(100L, scope.getSpan().getSpanId().getHigh());
-    Assert.assertNotEquals(0L, scope.getSpan().getSpanId().getLow());
-    scope.close();
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java b/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
deleted file mode 100644
index 7ca897f..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
+++ /dev/null
@@ -1,62 +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.apache.htrace.core;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Test;
-
-public class TestHTraceConfiguration {
-  @Test
-  public void testGetBoolean() throws Exception {
-
-    Map<String, String> m = new HashMap<String, String>();
-    m.put("testTrue", " True");
-    m.put("testFalse", "falsE ");
-    HTraceConfiguration configuration = HTraceConfiguration.fromMap(m);
-
-    // Tests for value being there
-    assertTrue(configuration.getBoolean("testTrue", false));
-    assertFalse(configuration.getBoolean("testFalse", true));
-
-    // Test for absent
-    assertTrue(configuration.getBoolean("absent", true));
-    assertFalse(configuration.getBoolean("absent", false));
-  }
-
-  @Test
-  public void testGetInt() throws Exception {
-    Map<String, String> m = new HashMap<String, String>();
-    m.put("a", "100");
-    m.put("b", "0");
-    m.put("c", "-100");
-    m.put("d", "5");
-
-    HTraceConfiguration configuration = HTraceConfiguration.fromMap(m);
-    assertEquals(100, configuration.getInt("a", -999));
-    assertEquals(0, configuration.getInt("b", -999));
-    assertEquals(-100, configuration.getInt("c", -999));
-    assertEquals(5, configuration.getInt("d", -999));
-    assertEquals(-999, configuration.getInt("absent", -999));
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java b/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
deleted file mode 100644
index 9388707..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
+++ /dev/null
@@ -1,65 +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.apache.htrace.core;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.IOException;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Test;
-
-public class TestLocalFileSpanReceiver {
-  @Test
-  public void testUniqueLocalTraceFileName() {
-    String filename1 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    String filename2 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    boolean eq = filename1.equals(filename2);
-    if (System.getProperty("os.name").startsWith("Linux")) {
-      // ${java.io.tmpdir}/[pid]
-      assertTrue(eq);
-    } else {
-      // ${java.io.tmpdir}/[random UUID]
-      assertFalse(eq);
-    }
-  }
-
-  @Test
-  public void testWriteToLocalFile() throws IOException {
-    String traceFileName = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    Tracer tracer = new Tracer.Builder().
-        name("testWriteToLocalFileTracer").
-        tracerPool(new TracerPool("testWriteToLocalFile")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "sampler.classes", "AlwaysSampler",
-            "span.receiver.classes", LocalFileSpanReceiver.class.getName(),
-            "local.file.span.receiver.path", traceFileName,
-            "tracer.id", "%{tname}")).
-        build();
-    TraceScope scope = tracer.newScope("testWriteToLocalFile");
-    scope.close();
-    tracer.close();
-
-    ObjectMapper mapper = new ObjectMapper();
-    MilliSpan span = mapper.readValue(new File(traceFileName), MilliSpan.class);
-    assertEquals("testWriteToLocalFile", span.getDescription());
-    assertEquals("testWriteToLocalFileTracer", span.getTracerId());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java b/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java
deleted file mode 100644
index 7ce1fdb..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestMilliSpan.java
+++ /dev/null
@@ -1,145 +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.apache.htrace.core;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-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.getSpanId(), got.getSpanId());
-    assertEquals(expected.getTracerId(), got.getTracerId());
-    assertTrue(Arrays.equals(expected.getParents(), got.getParents()));
-    Map<String, String> expectedT = expected.getKVAnnotations();
-    Map<String, String> gotT = got.getKVAnnotations();
-    if (expectedT == null) {
-      assertEquals(null, gotT);
-    } else {
-      assertEquals(expectedT.size(), gotT.size());
-      for (String key : expectedT.keySet()) {
-        assertEquals(expectedT.get(key), gotT.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 SpanId[] { new SpanId(7L, 7L) }).
-        tracerId("b2404.halxg.com:8080").
-        spanId(new SpanId(7L, 8L)).
-        build();
-    String json = span.toJson();
-    MilliSpan dspan = MilliSpan.fromJson(json);
-    compareSpans(span, dspan);
-  }
-
-  @Test
-  public void testJsonSerializationWithNegativeLongValue() throws Exception {
-    MilliSpan span = new MilliSpan.Builder().
-        description("foospan").
-        begin(-1L).
-        end(-1L).
-        parents(new SpanId[] { new SpanId(-1L, -1L) }).
-        tracerId("b2404.halxg.com:8080").
-        spanId(new SpanId(-1L, -2L)).
-        build();
-    String json = span.toJson();
-    MilliSpan dspan = MilliSpan.fromJson(json);
-    compareSpans(span, dspan);
-  }
-
-  @Test
-  public void testJsonSerializationWithRandomLongValue() throws Exception {
-    SpanId parentId = SpanId.fromRandom();
-    MilliSpan span = new MilliSpan.Builder().
-        description("foospan").
-        begin(ThreadLocalRandom.current().nextLong()).
-        end(ThreadLocalRandom.current().nextLong()).
-        parents(new SpanId[] { parentId }).
-        tracerId("b2404.halxg.com:8080").
-        spanId(parentId.newChildId()).
-        build();
-    String json = span.toJson();
-    MilliSpan dspan = MilliSpan.fromJson(json);
-    compareSpans(span, dspan);
-  }
-
-  @Test
-  public void testJsonSerializationWithOptionalFields() throws Exception {
-    MilliSpan.Builder builder = new MilliSpan.Builder().
-        description("foospan").
-        begin(300).
-        end(400).
-        parents(new SpanId[] { }).
-        tracerId("b2408.halxg.com:8080").
-        spanId(new SpanId(111111111L, 111111111L));
-    Map<String, String> traceInfo = new HashMap<String, String>();
-    traceInfo.put("abc", "123");
-    traceInfo.put("def", "456");
-    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();
-    MilliSpan dspan = MilliSpan.fromJson(json);
-    compareSpans(span, dspan);
-  }
-
-  @Test
-  public void testJsonSerializationWithFieldsNotSet() throws Exception {
-    MilliSpan span = new MilliSpan.Builder().build();
-    String json = span.toJson();
-    MilliSpan dspan = MilliSpan.fromJson(json);
-    compareSpans(span, dspan);
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java b/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java
deleted file mode 100644
index c8ed7f1..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java
+++ /dev/null
@@ -1,43 +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.apache.htrace.core;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestNullScope {
-  private void verifyNullScope(TraceScope scope) {
-    Assert.assertTrue(null == scope.getSpan());
-    Assert.assertFalse(scope.detached);
-    scope.detach();
-    Assert.assertTrue(scope.detached);
-    scope.reattach();
-    Assert.assertFalse(scope.detached);
-    scope.close();
-  }
-
-  @Test
-  public void testNullScope() {
-    Tracer tracer = new Tracer.Builder().
-        name("testNullScope").
-        tracerPool(new TracerPool("testNullScope")).
-        conf(HTraceConfiguration.EMPTY).
-        build();
-    verifyNullScope(tracer.newScope("testNullScope"));
-    verifyNullScope(tracer.newNullScope());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java
deleted file mode 100644
index 2305d9f..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java
+++ /dev/null
@@ -1,100 +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.apache.htrace.core;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestSampler {
-  private Sampler[] getSamplersFromConf(HTraceConfiguration conf) {
-    Tracer tracer = new Tracer.Builder().
-        name("MyTracer").
-        tracerPool(new TracerPool("getSamplersFromConf")).
-        conf(conf).
-        build();
-    Sampler[] samplers = tracer.getSamplers();
-    tracer.close();
-    return samplers;
-  }
-
-  private void checkArrayContains(List<Class<? extends Sampler>> expected,
-                                  Sampler[] samplers) {
-    for (Iterator<Class<? extends Sampler>> iter = expected.iterator();
-         iter.hasNext(); ) {
-      Class<? extends Sampler> samplerClass = iter.next();
-      boolean found = false;
-      for (int i = 0; i < samplers.length; i++) {
-        if (samplers[i] != null) {
-          if (samplers[i].getClass().equals(samplerClass)) {
-            samplers[i] = null;
-            found = true;
-            break;
-          }
-        }
-      }
-      Assert.assertTrue("Failed to find sampler class " +
-          samplerClass.getName(), found);
-    }
-    for (int i = 0; i < samplers.length; i++) {
-      if (samplers[i] != null) {
-        Assert.fail("Got extra sampler of type " +
-            samplers.getClass().getName());
-      }
-    }
-  }
-
-  private void checkArrayContains(Class<? extends Sampler> expected, Sampler[] samplers) {
-    LinkedList<Class<? extends Sampler>> expectedList =
-        new LinkedList<Class<? extends Sampler>>();
-    expectedList.add(expected);
-    checkArrayContains(expectedList, samplers);
-  }
-
-  @Test
-  public void testTracerBuilderCreatesCorrectSamplers() {
-    Sampler[] samplers = getSamplersFromConf(HTraceConfiguration.
-        fromKeyValuePairs("sampler.classes", "AlwaysSampler"));
-    checkArrayContains(AlwaysSampler.class, samplers);
-
-    samplers = getSamplersFromConf(HTraceConfiguration.
-        fromKeyValuePairs("sampler.classes", "NeverSampler"));
-    checkArrayContains(NeverSampler.class, samplers);
-
-    samplers = getSamplersFromConf(HTraceConfiguration.
-        fromKeyValuePairs("sampler.classes", "NonExistentSampler"));
-    Assert.assertEquals(0, samplers.length);
-
-    samplers = getSamplersFromConf(HTraceConfiguration.EMPTY);
-    Assert.assertEquals(0, samplers.length);
-  }
-
-  @Test
-  public void testAlwaysSampler() {
-    AlwaysSampler sampler = new AlwaysSampler(HTraceConfiguration.EMPTY);
-    Assert.assertTrue(sampler.next());
-  }
-
-  @Test
-  public void testNeverSampler() {
-    NeverSampler sampler = new NeverSampler(HTraceConfiguration.EMPTY);
-    Assert.assertTrue(!sampler.next());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java
deleted file mode 100644
index bb57368..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanId.java
+++ /dev/null
@@ -1,72 +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.apache.htrace.core;
-
-import java.util.Random;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestSpanId {
-  private void testRoundTrip(SpanId id) throws Exception {
-    String str = id.toString();
-    SpanId id2 = SpanId.fromString(str);
-    Assert.assertEquals(id, id2);
-  }
-
-  @Test
-  public void testToStringAndFromString() throws Exception {
-    testRoundTrip(SpanId.INVALID);
-    testRoundTrip(new SpanId(0x1234567812345678L, 0x1234567812345678L));
-    testRoundTrip(new SpanId(0xf234567812345678L, 0xf234567812345678L));
-    testRoundTrip(new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL));
-    Random rand = new Random(12345);
-    for (int i = 0; i < 100; i++) {
-      testRoundTrip(new SpanId(rand.nextLong(), rand.nextLong()));
-    }
-  }
-
-  @Test
-  public void testValidAndInvalidIds() throws Exception {
-    Assert.assertFalse(SpanId.INVALID.isValid());
-    Assert.assertTrue(
-        new SpanId(0x1234567812345678L, 0x1234567812345678L).isValid());
-    Assert.assertTrue(
-        new SpanId(0xf234567812345678L, 0xf234567812345678L).isValid());
-  }
-
-  private void expectLessThan(SpanId a, SpanId b) throws Exception {
-    int cmp = a.compareTo(b);
-    Assert.assertTrue("Expected " + a + " to be less than " + b,
-        (cmp < 0));
-    int cmp2 = b.compareTo(a);
-    Assert.assertTrue("Expected " + b + " to be greater than " + a,
-        (cmp2 > 0));
-  }
-
-  @Test
-  public void testIdComparisons() throws Exception {
-    expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L),
-                   new SpanId(0x0000000000000001L, 0x0000000000000002L));
-    expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L),
-                   new SpanId(0x0000000000000002L, 0x0000000000000000L));
-    expectLessThan(SpanId.INVALID,
-                   new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL));
-    expectLessThan(new SpanId(0x1234567812345678L, 0x1234567812345678L),
-                   new SpanId(0x1234567812345678L, 0xf234567812345678L));
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java b/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
deleted file mode 100644
index b97d624..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
+++ /dev/null
@@ -1,127 +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.apache.htrace.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.Assert;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-public class TestSpanReceiverBuilder {
-  private static final Log LOG =
-      LogFactory.getLog(TestSpanReceiverBuilder.class);
-
-  private List<SpanReceiver> createSpanReceivers(String classes) {
-    Tracer tracer = new Tracer.Builder().
-        name("MyTracer").
-        tracerPool(new TracerPool("createSpanReceivers")).
-        conf(HTraceConfiguration.fromKeyValuePairs(
-            "span.receiver.classes", classes)).
-        build();
-    SpanReceiver[] receivers = tracer.getTracerPool().getReceivers();
-    tracer.close();
-    LinkedList<SpanReceiver> receiverList = new LinkedList<SpanReceiver>();
-    for (SpanReceiver item: receivers) {
-      receiverList.add(item);
-    }
-    return receiverList;
-  }
-
-  @Test
-  public void TestCreateStandardSpanReceivers() {
-    List<SpanReceiver> receivers;
-    receivers = createSpanReceivers("");
-    Assert.assertTrue(receivers.isEmpty());
-    receivers = createSpanReceivers("POJOSpanReceiver");
-    Assert.assertTrue(receivers.get(0).getClass().getName().
-        equals("org.apache.htrace.core.POJOSpanReceiver"));
-    receivers = createSpanReceivers(
-               "org.apache.htrace.core.StandardOutSpanReceiver");
-    Assert.assertTrue(receivers.get(0).getClass().getName().
-        equals("org.apache.htrace.core.StandardOutSpanReceiver"));
-    receivers = createSpanReceivers(
-               "POJOSpanReceiver;StandardOutSpanReceiver");
-    Assert.assertEquals(2, receivers.size());
-    for (Iterator<SpanReceiver> iter = receivers.iterator(); iter.hasNext();) {
-      SpanReceiver receiver = iter.next();
-      if (receiver.getClass().getName().equals(
-          "org.apache.htrace.core.POJOSpanReceiver")) {
-        iter.remove();
-        break;
-      }
-    }
-    for (Iterator<SpanReceiver> iter = receivers.iterator(); iter.hasNext();) {
-      SpanReceiver receiver = iter.next();
-      if (receiver.getClass().getName().equals(
-          "org.apache.htrace.core.StandardOutSpanReceiver")) {
-        iter.remove();
-        break;
-      }
-    }
-    Assert.assertEquals(0, receivers.size());
-  }
-
-  public static class GoodSpanReceiver extends SpanReceiver {
-    public GoodSpanReceiver(HTraceConfiguration conf) {
-    }
-
-    @Override
-    public void receiveSpan(Span span) {
-    }
-
-    @Override
-    public void close() throws IOException {
-    }
-  }
-
-  public static class BadSpanReceiver extends SpanReceiver {
-    public BadSpanReceiver(HTraceConfiguration conf) {
-      throw new RuntimeException("Can't create BadSpanReceiver");
-    }
-
-    @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 {
-    List<SpanReceiver> receivers;
-    receivers = createSpanReceivers(
-        GoodSpanReceiver.class.getName());
-    Assert.assertEquals(1, receivers.size());
-    Assert.assertTrue(receivers.get(0).getClass().getName().
-        contains("GoodSpanReceiver"));
-    receivers = createSpanReceivers(
-        BadSpanReceiver.class.getName());
-    Assert.assertEquals(0, receivers.size());
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java b/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java
deleted file mode 100644
index 1e842c5..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java
+++ /dev/null
@@ -1,52 +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.apache.htrace.core;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-
-import org.junit.Test;
-
-public class TestTracerId {
-  private void testTracerIdImpl(String expected, String fmt) {
-    assertEquals(expected, new TracerId(
-        HTraceConfiguration.fromKeyValuePairs(TracerId.TRACER_ID_KEY, fmt),
-        "TracerName").get());
-  }
-
-  @Test
-  public void testSimpleTracerIds() {
-    testTracerIdImpl("abc", "abc");
-    testTracerIdImpl("abc", "a\\bc");
-    testTracerIdImpl("abc", "ab\\c");
-    testTracerIdImpl("abc", "\\a\\b\\c");
-    testTracerIdImpl("a\\bc", "a\\\\bc");
-  }
-
-  @Test
-  public void testSubstitutionVariables() throws IOException {
-    testTracerIdImpl("myTracerName", "my%{tname}");
-    testTracerIdImpl(TracerId.getProcessName(), "%{pname}");
-    testTracerIdImpl("my." + TracerId.getProcessName(), "my.%{pname}");
-    testTracerIdImpl(TracerId.getBestIpString() + ".str", "%{ip}.str");
-    testTracerIdImpl("%{pname}", "\\%{pname}");
-    testTracerIdImpl("%cash%money{}", "%cash%money{}");
-    testTracerIdImpl("Foo." + Long.valueOf(TracerId.getOsPid()).toString(),
-        "Foo.%{pid}");
-  }
-}


[3/5] incubator-htrace git commit: HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)

Posted by cm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java b/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java
deleted file mode 100644
index b843999..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java
+++ /dev/null
@@ -1,141 +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.apache.htrace.core;
-
-import java.util.Random;
-import java.util.concurrent.ThreadLocalRandom;
-
-/**
- * Does some stuff and traces it.
- */
-public class TraceCreator {
-  public static final String RPC_TRACE_ROOT = "createSampleRpcTrace";
-  public static final String THREADED_TRACE_ROOT = "createThreadedTrace";
-  public static final String SIMPLE_TRACE_ROOT = "createSimpleTrace";
-
-  private final Tracer tracer;
-
-  public TraceCreator(Tracer tracer) {
-    this.tracer = tracer;
-  }
-
-  public void createSampleRpcTrace() {
-    TraceScope s = tracer.newScope(RPC_TRACE_ROOT);
-    try {
-      pretendRpcSend();
-    } finally {
-      s.close();
-    }
-  }
-
-  public void createSimpleTrace() {
-    TraceScope s = tracer.newScope(SIMPLE_TRACE_ROOT);
-    try {
-      importantWork1();
-    } finally {
-      s.close();
-    }
-  }
-
-  /**
-   * Creates the demo trace (will create different traces from call to call).
-   */
-  public void createThreadedTrace() {
-    TraceScope s = tracer.newScope(THREADED_TRACE_ROOT);
-    try {
-      Random r = ThreadLocalRandom.current();
-      int numThreads = r.nextInt(4) + 1;
-      Thread[] threads = new Thread[numThreads];
-
-      for (int i = 0; i < numThreads; i++) {
-        threads[i] = new Thread(tracer.wrap(new MyRunnable(), null));
-      }
-      for (int i = 0; i < numThreads; i++) {
-        threads[i].start();
-      }
-      for (int i = 0; i < numThreads; i++) {
-        try {
-          threads[i].join();
-        } catch (InterruptedException e) {
-        }
-      }
-      importantWork1();
-    } finally {
-      s.close();
-    }
-  }
-
-  private void importantWork1() {
-    TraceScope cur = tracer.newScope("important work 1");
-    try {
-      Thread.sleep((long) (2000 * Math.random()));
-      importantWork2();
-    } catch (InterruptedException e) {
-      Thread.currentThread().interrupt();
-    } finally {
-      cur.close();
-    }
-  }
-
-  private void importantWork2() {
-    TraceScope cur = tracer.newScope("important work 2");
-    try {
-      Thread.sleep((long) (2000 * Math.random()));
-    } catch (InterruptedException e) {
-      Thread.currentThread().interrupt();
-    } finally {
-      cur.close();
-    }
-  }
-
-  private class MyRunnable implements Runnable {
-    @Override
-    public void run() {
-      try {
-        Thread.sleep(750);
-        Random r = ThreadLocalRandom.current();
-        int importantNumber = 100 / r.nextInt(3);
-        System.out.println("Important number: " + importantNumber);
-      } catch (InterruptedException ie) {
-        Thread.currentThread().interrupt();
-      } catch (ArithmeticException ae) {
-        TraceScope c = tracer.newScope("dealing with arithmetic exception.");
-        try {
-          Thread.sleep((long) (3000 * Math.random()));
-        } catch (InterruptedException ie1) {
-          Thread.currentThread().interrupt();
-        } finally {
-          c.close();
-        }
-      }
-    }
-  }
-
-  public void pretendRpcSend() {
-    Span span = tracer.getCurrentSpan();
-    pretendRpcReceiveWithTraceInfo(span.getSpanId());
-  }
-
-  public void pretendRpcReceiveWithTraceInfo(SpanId parentId) {
-    TraceScope s = tracer.newScope("received RPC", parentId);
-    try {
-      importantWork1();
-    } finally {
-      s.close();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java b/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java
deleted file mode 100644
index a06e620..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/core/TraceGraph.java
+++ /dev/null
@@ -1,176 +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.apache.htrace.core;
-
-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.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.TreeSet;
-
-/**
- * Used to create the graph formed by spans.
- */
-public class TraceGraph {
-  private static final Log LOG = LogFactory.getLog(Tracer.class);
-
-
-  public static class SpansByParent {
-    /**
-     * Compare two spans by span ID.
-     */
-    private static Comparator<Span> COMPARATOR =
-        new Comparator<Span>() {
-          @Override
-          public int compare(Span a, Span b) {
-            return a.getSpanId().compareTo(b.getSpanId());
-          }
-        };
-
-    private final TreeSet<Span> treeSet;
-
-    private final HashMap<SpanId, LinkedList<Span>> parentToSpans;
-
-    SpansByParent(Collection<Span> spans) {
-      TreeSet<Span> treeSet = new TreeSet<Span>(COMPARATOR);
-      parentToSpans = new HashMap<SpanId, LinkedList<Span>>();
-      for (Span span : spans) {
-        treeSet.add(span);
-        for (SpanId parent : span.getParents()) {
-          LinkedList<Span> list = parentToSpans.get(parent);
-          if (list == null) {
-            list = new LinkedList<Span>();
-            parentToSpans.put(parent, list);
-          }
-          list.add(span);
-        }
-        if (span.getParents().length == 0) {
-          LinkedList<Span> list = parentToSpans.get(SpanId.INVALID);
-          if (list == null) {
-            list = new LinkedList<Span>();
-            parentToSpans.put(SpanId.INVALID, list);
-          }
-          list.add(span);
-        }
-      }
-      this.treeSet = treeSet;
-    }
-
-    public List<Span> find(SpanId parentId) {
-      LinkedList<Span> spans = parentToSpans.get(parentId);
-      if (spans == null) {
-        return new LinkedList<Span>();
-      }
-      return spans;
-    }
-
-    public Iterator<Span> iterator() {
-      return Collections.unmodifiableSortedSet(treeSet).iterator();
-    }
-  }
-
-  public static class SpansByTracerId {
-    /**
-     * Compare two spans by process ID, and then by span ID.
-     */
-    private static Comparator<Span> COMPARATOR =
-        new Comparator<Span>() {
-          @Override
-          public int compare(Span a, Span b) {
-            int cmp = a.getTracerId().compareTo(b.getTracerId());
-            if (cmp != 0) {
-              return cmp;
-            }
-            return a.getSpanId().compareTo(b.getSpanId());
-          }
-        };
-
-    private final TreeSet<Span> treeSet;
-
-    SpansByTracerId(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 tracerId) {
-      List<Span> spans = new ArrayList<Span>();
-      Span span = new MilliSpan.Builder().
-                    spanId(SpanId.INVALID).
-                    tracerId(tracerId).
-                    build();
-      while (true) {
-        span = treeSet.higher(span);
-        if (span == null) {
-          break;
-        }
-        if (span.getTracerId().equals(tracerId)) {
-          break;
-        }
-        spans.add(span);
-      }
-      return spans;
-    }
-
-    public Iterator<Span> iterator() {
-      return Collections.unmodifiableSortedSet(treeSet).iterator();
-    }
-  }
-
-  private final SpansByParent spansByParent;
-  private final SpansByTracerId spansByTracerId;
-
-  /**
-   * Create a new TraceGraph
-   *
-   * @param spans The collection of spans to use to create this TraceGraph. Should
-   *              have at least one root span.
-   */
-  public TraceGraph(Collection<Span> spans) {
-    this.spansByParent = new SpansByParent(spans);
-    this.spansByTracerId = new SpansByTracerId(spans);
-  }
-
-  public SpansByParent getSpansByParent() {
-    return spansByParent;
-  }
-
-  public SpansByTracerId getSpansByTracerId() {
-    return spansByTracerId;
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder bld = new StringBuilder();
-    String prefix = "";
-    for (Iterator<Span> iter = spansByParent.iterator(); iter.hasNext();) {
-      Span span = iter.next();
-      bld.append(prefix).append(span.toString());
-      prefix = "\n";
-    }
-    return bld.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java b/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java
deleted file mode 100644
index 7cb4aed..0000000
--- a/htrace-core/src/test/java/org/apache/htrace/util/TestUtil.java
+++ /dev/null
@@ -1,91 +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.apache.htrace.util;
-
-import java.io.File;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Utilities for writing unit tests.
- */
-public class TestUtil {
-  /**
-   * Get a dump of the stack traces of all threads.
-   */
-  public static String threadDump() {
-    StringBuilder dump = new StringBuilder();
-    Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
-    for (Map.Entry<Thread, StackTraceElement[]> e : stackTraces.entrySet()) {
-      Thread thread = e.getKey();
-      dump.append(String.format(
-          "\"%s\" %s prio=%d tid=%d %s\njava.lang.Thread.State: %s",
-          thread.getName(),
-          (thread.isDaemon() ? "daemon" : ""),
-          thread.getPriority(),
-          thread.getId(),
-          Thread.State.WAITING.equals(thread.getState()) ?
-              "in Object.wait()" : thread.getState().name().toLowerCase(),
-          Thread.State.WAITING.equals(thread.getState()) ?
-              "WAITING (on object monitor)" : thread.getState()));
-      for (StackTraceElement stackTraceElement : e.getValue()) {
-        dump.append("\n        at ");
-        dump.append(stackTraceElement);
-      }
-      dump.append("\n");
-    }
-    return dump.toString();
-  }
-
-  /**
-   * A callback which returns a value of type T.
-   *
-   * TODO: remove this when we're on Java 8, in favor of
-   * java.util.function.Supplier.
-   */
-  public interface Supplier<T> {
-    T get();
-  }
-
-  /**
-   * Wait for a condition to become true for a configurable amount of time.
-   *
-   * @param check           The condition to wait for.
-   * @param periodMs        How often to check the condition, in milliseconds.
-   * @param timeoutMs       How long to wait in total, in milliseconds.
-   */
-  public static void waitFor(Supplier<Boolean> check, 
-      long periodMs, long timeoutMs)
-          throws TimeoutException, InterruptedException
-  {
-    long endNs = System.nanoTime() +
-        TimeUnit.NANOSECONDS.convert(timeoutMs, TimeUnit.MILLISECONDS);
-    while (true) {
-      boolean result = check.get();
-      if (result) {
-        return;
-      }
-      long nowNs = System.nanoTime();
-      if (nowNs >= endNs) {
-        throw new TimeoutException("Timed out waiting for test condition. " +
-            "Thread dump:\n" + threadDump());
-      }
-      Thread.sleep(periodMs);
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/pom.xml
----------------------------------------------------------------------
diff --git a/htrace-core4/pom.xml b/htrace-core4/pom.xml
new file mode 100644
index 0000000..b5d3c74
--- /dev/null
+++ b/htrace-core4/pom.xml
@@ -0,0 +1,125 @@
+<?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-core4</artifactId>
+  <packaging>jar</packaging>
+
+  <parent>
+    <artifactId>htrace</artifactId>
+    <groupId>org.apache.htrace</groupId>
+    <version>4.1.0-incubating-SNAPSHOT</version>
+    <relativePath>..</relativePath>
+  </parent>
+
+  <name>htrace-core4</name>
+  <url>http://incubator.apache.org/projects/htrace.html</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </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-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>
+      <plugin>
+        <!-- explicitly define maven-deploy-plugin after other to force exec order -->
+        <artifactId>maven-deploy-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <!-- Global deps. -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <!-- core specific deps. -->
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+  </dependencies>
+
+  <profiles>
+    <profile>
+      <id>dist</id>
+      <build>
+        <plugins>
+          <plugin>
+            <!--Make it so assembly:single does nothing in here-->
+            <artifactId>maven-assembly-plugin</artifactId>
+            <configuration>
+              <skipAssembly>true</skipAssembly>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java
new file mode 100644
index 0000000..8d5a296
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/AlwaysSampler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.core;
+
+/**
+ * A Sampler that always returns true.
+ */
+public final class AlwaysSampler extends Sampler {
+  public static final AlwaysSampler INSTANCE = new AlwaysSampler(null);
+
+  public AlwaysSampler(HTraceConfiguration conf) {
+  }
+
+  @Override
+  public boolean next() {
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java
new file mode 100644
index 0000000..5a838c7
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/CountSampler.java
@@ -0,0 +1,39 @@
+/*
+ * 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.core;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * 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 extends Sampler {
+  public final static String SAMPLER_FREQUENCY_CONF_KEY = "sampler.frequency";
+
+  final long frequency;
+  long count = ThreadLocalRandom.current().nextLong();
+
+  public CountSampler(HTraceConfiguration conf) {
+    this.frequency = Long.parseLong(conf.get(SAMPLER_FREQUENCY_CONF_KEY), 10);
+  }
+
+  @Override
+  public boolean next() {
+    return (count++ % frequency) == 0;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java b/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
new file mode 100644
index 0000000..c6e445b
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/HTraceConfiguration.java
@@ -0,0 +1,109 @@
+/*
+ * 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.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Wrapper which integrating applications should implement in order
+ * to provide tracing configuration.
+ */
+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);
+  }
+
+  public 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);
+
+  public boolean getBoolean(String key, boolean defaultValue) {
+    String value = get(key, String.valueOf(defaultValue)).trim().toLowerCase();
+
+    if ("true".equals(value)) {
+      return true;
+    } else if ("false".equals(value)) {
+      return false;
+    }
+
+    LOG.warn("Expected boolean for key [" + key + "] instead got [" + value + "].");
+    return defaultValue;
+  }
+
+  public int getInt(String key, int defaultVal) {
+    String val = get(key);
+    if (val == null || val.trim().isEmpty()) {
+      return defaultVal;
+    }
+    try {
+      return Integer.parseInt(val);
+    } catch (NumberFormatException nfe) {
+      throw new IllegalArgumentException("Bad value for '" + key + "': should be int");
+    }
+  }
+
+  private static class MapConf extends HTraceConfiguration {
+    private final Map<String, String> conf;
+
+    public MapConf(Map<String, String> conf) {
+      this.conf = new HashMap<String, String>(conf);
+    }
+
+    @Override
+    public String get(String key) {
+      return conf.get(key);
+    }
+
+    @Override
+    public String get(String key, String defaultValue) {
+      String value = get(key);
+      return value == null ? defaultValue : value;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
new file mode 100644
index 0000000..69a43b1
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/LocalFileSpanReceiver.java
@@ -0,0 +1,257 @@
+/*
+ * 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.core;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.FileSystems;
+import java.nio.file.StandardOpenOption;
+import java.util.UUID;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Writes the spans it receives to a local file.
+ */
+public class LocalFileSpanReceiver extends SpanReceiver {
+  private static final Log LOG = LogFactory.getLog(LocalFileSpanReceiver.class);
+  public static final String PATH_KEY = "local.file.span.receiver.path";
+  public static final String CAPACITY_KEY = "local.file.span.receiver.capacity";
+  public static final int CAPACITY_DEFAULT = 5000;
+  private static ObjectWriter JSON_WRITER = new ObjectMapper().writer();
+  private final String path;
+
+  private byte[][] bufferedSpans;
+  private int bufferedSpansIndex;
+  private final ReentrantLock bufferLock = new ReentrantLock();
+
+  private final FileOutputStream stream;
+  private final FileChannel channel;
+  private final ReentrantLock channelLock = new ReentrantLock();
+
+  public LocalFileSpanReceiver(HTraceConfiguration conf) {
+    int capacity = conf.getInt(CAPACITY_KEY, CAPACITY_DEFAULT);
+    if (capacity < 1) {
+      throw new IllegalArgumentException(CAPACITY_KEY + " must not be " +
+          "less than 1.");
+    }
+    String pathStr = conf.get(PATH_KEY);
+    if (pathStr == null || pathStr.isEmpty()) {
+      path = getUniqueLocalTraceFileName();
+    } else {
+      path = pathStr;
+    }
+    boolean success = false;
+    try {
+      this.stream = new FileOutputStream(path, true);
+    } catch (IOException ioe) {
+      LOG.error("Error opening " + path + ": " + ioe.getMessage());
+      throw new RuntimeException(ioe);
+    }
+    this.channel = stream.getChannel();
+    if (this.channel == null) {
+      try {
+        this.stream.close();
+      } catch (IOException e) {
+        LOG.error("Error closing " + path, e);
+      }
+      LOG.error("Failed to get channel for " + path);
+      throw new RuntimeException("Failed to get channel for " + path);
+    }
+    this.bufferedSpans = new byte[capacity][];
+    this.bufferedSpansIndex = 0;
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Created new LocalFileSpanReceiver with path = " + path +
+                ", capacity = " + capacity);
+    }
+  }
+
+  /**
+   * Number of buffers to use in FileChannel#write.
+   *
+   * On UNIX, FileChannel#write uses writev-- a kernel interface that allows
+   * us to send multiple buffers at once.  This is more efficient than making a
+   * separate write call for each buffer, since it minimizes the number of
+   * transitions from userspace to kernel space.
+   */
+  private final int WRITEV_SIZE = 20;
+
+  private final static ByteBuffer newlineBuf = 
+      ByteBuffer.wrap(new byte[] { (byte)0xa });
+
+  /**
+   * Flushes a bufferedSpans array.
+   */
+  private void doFlush(byte[][] toFlush, int len) throws IOException {
+    int bidx = 0, widx = 0;
+    ByteBuffer writevBufs[] = new ByteBuffer[2 * WRITEV_SIZE];
+
+    while (true) {
+      if (widx == writevBufs.length) {
+        channel.write(writevBufs);
+        widx = 0;
+      }
+      if (bidx == len) {
+        break;
+      }
+      writevBufs[widx] = ByteBuffer.wrap(toFlush[bidx]);
+      writevBufs[widx + 1] = newlineBuf;
+      bidx++;
+      widx+=2;
+    }
+    if (widx > 0) {
+      channel.write(writevBufs, 0, widx);
+    }
+  }
+
+  @Override
+  public void receiveSpan(Span span) {
+    // Serialize the span data into a byte[].  Note that we're not holding the
+    // lock here, to improve concurrency.
+    byte jsonBuf[] = null;
+    try {
+      jsonBuf = JSON_WRITER.writeValueAsBytes(span);
+    } catch (JsonProcessingException e) {
+        LOG.error("receiveSpan(path=" + path + ", span=" + span + "): " +
+                  "Json processing error: " + e.getMessage());
+      return;
+    }
+
+    // Grab the bufferLock and put our jsonBuf into the list of buffers to
+    // flush. 
+    byte toFlush[][] = null;
+    bufferLock.lock();
+    try {
+      if (bufferedSpans == null) {
+        LOG.debug("receiveSpan(path=" + path + ", span=" + span + "): " +
+                  "LocalFileSpanReceiver for " + path + " is closed.");
+        return;
+      }
+      bufferedSpans[bufferedSpansIndex] = jsonBuf;
+      bufferedSpansIndex++;
+      if (bufferedSpansIndex == bufferedSpans.length) {
+        // If we've hit the limit for the number of buffers to flush, 
+        // swap out the existing bufferedSpans array for a new array, and
+        // prepare to flush those spans to disk.
+        toFlush = bufferedSpans;
+        bufferedSpansIndex = 0;
+        bufferedSpans = new byte[bufferedSpans.length][];
+      }
+    } finally {
+      bufferLock.unlock();
+    }
+    if (toFlush != null) {
+      // We released the bufferLock above, to avoid blocking concurrent
+      // receiveSpan calls.  But now, we must take the channelLock, to make
+      // sure that we have sole access to the output channel.  If we did not do
+      // this, we might get interleaved output.
+      //
+      // There is a small chance that another thread doing a flush of more
+      // recent spans could get ahead of us here, and take the lock before we
+      // do.  This is ok, since spans don't have to be written out in order.
+      channelLock.lock();
+      try {
+        doFlush(toFlush, toFlush.length);
+      } catch (IOException ioe) {
+        LOG.error("Error flushing buffers to " + path + ": " +
+            ioe.getMessage());
+      } finally {
+        channelLock.unlock();
+      }
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    byte toFlush[][] = null;
+    int numToFlush = 0;
+    bufferLock.lock();
+    try {
+      if (bufferedSpans == null) {
+        LOG.info("LocalFileSpanReceiver for " + path + " was already closed.");
+        return;
+      }
+      numToFlush = bufferedSpansIndex;
+      bufferedSpansIndex = 0;
+      toFlush = bufferedSpans;
+      bufferedSpans = null;
+    } finally {
+      bufferLock.unlock();
+    }
+    channelLock.lock();
+    try {
+      doFlush(toFlush, numToFlush);
+    } catch (IOException ioe) {
+      LOG.error("Error flushing buffers to " + path + ": " +
+          ioe.getMessage());
+    } finally {
+      try {
+        stream.close();
+      } catch (IOException e) {
+        LOG.error("Error closing stream for " + path, e);
+      }
+      channelLock.unlock();
+    }
+  }
+
+  public static String getUniqueLocalTraceFileName() {
+    String tmp = System.getProperty("java.io.tmpdir", "/tmp");
+    String nonce = null;
+    BufferedReader reader = null;
+    try {
+      // On Linux we can get a unique local file name by reading the process id
+      // out of /proc/self/stat.  (There isn't any portable way to get the
+      // process ID from Java.)
+      reader = new BufferedReader(
+          new InputStreamReader(new FileInputStream("/proc/self/stat"),
+                                "UTF-8"));
+      String line = reader.readLine();
+      if (line == null) {
+        throw new EOFException();
+      }
+      nonce = line.split(" ")[0];
+    } catch (IOException e) {
+    } finally {
+      if (reader != null) {
+        try {
+          reader.close();
+        } catch(IOException e) {
+          LOG.warn("Exception in closing " + reader, e);
+        }
+      }
+    }
+    if (nonce == null) {
+      // If we can't use the process ID, use a random nonce.
+      nonce = UUID.randomUUID().toString();
+    }
+    return new File(tmp, nonce).getAbsolutePath();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java b/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java
new file mode 100644
index 0000000..5dd6bdb
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/MilliSpan.java
@@ -0,0 +1,347 @@
+/*
+ * 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.core;
+
+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.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+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;
+
+/**
+ * A Span implementation that stores its information in milliseconds since the
+ * epoch.
+ */
+@JsonDeserialize(using = MilliSpan.MilliSpanDeserializer.class)
+public class MilliSpan implements Span {
+  private static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+  private static ObjectReader JSON_READER = OBJECT_MAPPER.reader(MilliSpan.class);
+  private static ObjectWriter JSON_WRITER = OBJECT_MAPPER.writer();
+  private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0];
+  private static final String EMPTY_STRING = "";
+
+  private long begin;
+  private long end;
+  private final String description;
+  private SpanId parents[];
+  private final SpanId spanId;
+  private Map<String, String> traceInfo = null;
+  private String tracerId;
+  private List<TimelineAnnotation> timeline = null;
+
+  @Override
+  public Span child(String childDescription) {
+    return new MilliSpan.Builder().
+      begin(System.currentTimeMillis()).
+      end(0).
+      description(childDescription).
+      parents(new SpanId[] {spanId}).
+      spanId(spanId.newChildId()).
+      tracerId(tracerId).
+      build();
+  }
+
+  /**
+   * The public interface for constructing a MilliSpan.
+   */
+  public static class Builder {
+    private long begin;
+    private long end;
+    private String description = EMPTY_STRING;
+    private SpanId parents[] = EMPTY_PARENT_ARRAY;
+    private SpanId spanId = SpanId.INVALID;
+    private Map<String, String> traceInfo = null;
+    private String tracerId = EMPTY_STRING;
+    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 parents(SpanId parents[]) {
+      this.parents = parents;
+      return this;
+    }
+
+    public Builder parents(List<SpanId> parentList) {
+      SpanId[] parents = new SpanId[parentList.size()];
+      for (int i = 0; i < parentList.size(); i++) {
+        parents[i] = parentList.get(i);
+      }
+      this.parents = parents;
+      return this;
+    }
+
+    public Builder spanId(SpanId spanId) {
+      this.spanId = spanId;
+      return this;
+    }
+
+    public Builder traceInfo(Map<String, String> traceInfo) {
+      this.traceInfo = traceInfo.isEmpty() ? null : traceInfo;
+      return this;
+    }
+
+    public Builder tracerId(String tracerId) {
+      this.tracerId = tracerId;
+      return this;
+    }
+
+    public Builder timeline(List<TimelineAnnotation> timeline) {
+      this.timeline = timeline.isEmpty() ? null : timeline;
+      return this;
+    }
+
+    public MilliSpan build() {
+      return new MilliSpan(this);
+    }
+  }
+
+  public MilliSpan() {
+    this.begin = 0;
+    this.end = 0;
+    this.description = EMPTY_STRING;
+    this.parents = EMPTY_PARENT_ARRAY;
+    this.spanId = SpanId.INVALID;
+    this.traceInfo = null;
+    this.tracerId = EMPTY_STRING;
+    this.timeline = null;
+  }
+
+  private MilliSpan(Builder builder) {
+    this.begin = builder.begin;
+    this.end = builder.end;
+    this.description = builder.description;
+    this.parents = builder.parents;
+    this.spanId = builder.spanId;
+    this.traceInfo = builder.traceInfo;
+    this.tracerId = builder.tracerId;
+    this.timeline = builder.timeline;
+  }
+
+  @Override
+  public synchronized void stop() {
+    if (end == 0) {
+      if (begin == 0)
+        throw new IllegalStateException("Span for " + description
+            + " has not been started");
+      end = System.currentTimeMillis();
+    }
+  }
+
+  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 SpanId getSpanId() {
+    return spanId;
+  }
+
+  @Override
+  public SpanId[] getParents() {
+    return parents;
+  }
+
+  @Override
+  public void setParents(SpanId[] parents) {
+    this.parents = parents;
+  }
+
+  @Override
+  public long getStartTimeMillis() {
+    return begin;
+  }
+
+  @Override
+  public long getStopTimeMillis() {
+    return end;
+  }
+
+  @Override
+  public void addKVAnnotation(String key, String value) {
+    if (traceInfo == null)
+      traceInfo = new HashMap<String, String>();
+    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<String, String> 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 getTracerId() {
+    return tracerId;
+  }
+
+  @Override
+  public void setTracerId(String tracerId) {
+    this.tracerId = tracerId;
+  }
+
+  @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();
+  }
+
+  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();
+      JsonNode bNode = root.get("b");
+      if (bNode != null) {
+        builder.begin(bNode.asLong());
+      }
+      JsonNode eNode = root.get("e");
+      if (eNode != null) {
+        builder.end(eNode.asLong());
+      }
+      JsonNode dNode = root.get("d");
+      if (dNode != null) {
+        builder.description(dNode.asText());
+      }
+      JsonNode sNode = root.get("a");
+      if (sNode != null) {
+        builder.spanId(SpanId.fromString(sNode.asText()));
+      }
+      JsonNode rNode = root.get("r");
+      if (rNode != null) {
+        builder.tracerId(rNode.asText());
+      }
+      JsonNode parentsNode = root.get("p");
+      LinkedList<SpanId> parents = new LinkedList<SpanId>();
+      if (parentsNode != null) {
+        for (Iterator<JsonNode> iter = parentsNode.elements();
+             iter.hasNext(); ) {
+          JsonNode parentIdNode = iter.next();
+          parents.add(SpanId.fromString(parentIdNode.asText()));
+        }
+      }
+      builder.parents(parents);
+      JsonNode traceInfoNode = root.get("n");
+      if (traceInfoNode != null) {
+        HashMap<String, String> traceInfo = new HashMap<String, String>();
+        for (Iterator<String> iter = traceInfoNode.fieldNames();
+             iter.hasNext(); ) {
+          String field = iter.next();
+          traceInfo.put(field, traceInfoNode.get(field).asText());
+        }
+        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();
+    }
+  }
+
+  public static MilliSpan fromJson(String json) throws IOException {
+    return JSON_READER.readValue(json);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java
new file mode 100644
index 0000000..60cc7d2
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/NeverSampler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.core;
+
+/**
+ * A Sampler that never returns true.
+ */
+public final class NeverSampler extends Sampler {
+  public static final NeverSampler INSTANCE = new NeverSampler(null);
+
+  public NeverSampler(HTraceConfiguration conf) {
+  }
+
+  @Override
+  public boolean next() {
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java b/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java
new file mode 100644
index 0000000..fe76e46
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/NullScope.java
@@ -0,0 +1,69 @@
+/*
+ * 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.core;
+
+/**
+ * An empty {@link TraceScope}.
+ */
+class NullScope extends TraceScope {
+  NullScope(Tracer tracer) {
+    super(tracer, null, null);
+  }
+
+  @Override
+  public SpanId getSpanId() {
+    return SpanId.INVALID;
+  }
+
+  @Override
+  public void detach() {
+    if (detached) {
+      Tracer.throwClientError("Can't detach this TraceScope  because " +
+          "it is already detached.");
+    }
+    detached = true;
+  }
+
+  @Override
+  public void reattach() {
+    if (!detached) {
+      Tracer.throwClientError("Can't reattach this TraceScope  because " +
+          "it is not detached.");
+    }
+    detached = false;
+  }
+
+  @Override
+  public void close() {
+    tracer.popNullScope();
+  }
+
+  @Override
+  public String toString() {
+    return "NullScope";
+  }
+
+  @Override
+  public void addKVAnnotation(String key, String value) {
+    // do nothing
+  }
+
+  @Override
+  public void addTimelineAnnotation(String msg) {
+    // do nothing
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
new file mode 100644
index 0000000..34322fa
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/POJOSpanReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * SpanReceiver for testing only that just collects the Span objects it
+ * receives. The spans it receives can be accessed with getSpans();
+ */
+public class POJOSpanReceiver extends SpanReceiver {
+  private final Collection<Span> spans;
+
+  public POJOSpanReceiver(HTraceConfiguration conf) {
+    this.spans = new HashSet<Span>();
+  }
+
+  /**
+   * @return The spans this POJOSpanReceiver has received.
+   */
+  public Collection<Span> getSpans() {
+    return spans;
+  }
+
+  @Override
+  public void close() throws IOException {
+  }
+
+  @Override
+  public void receiveSpan(Span span) {
+    spans.add(span);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
new file mode 100644
index 0000000..c0bb16c
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/ProbabilitySampler.java
@@ -0,0 +1,45 @@
+/*
+ * 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.core;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * 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 extends Sampler {
+  private static final Log LOG = LogFactory.getLog(ProbabilitySampler.class);
+  public final double threshold;
+  public final static String SAMPLER_FRACTION_CONF_KEY = "sampler.fraction";
+
+  public ProbabilitySampler(HTraceConfiguration conf) {
+    this.threshold = Double.parseDouble(conf.get(SAMPLER_FRACTION_CONF_KEY));
+    if (LOG.isTraceEnabled()) {
+      LOG.trace("Created new ProbabilitySampler with threshold = " +
+                threshold + ".");
+    }
+  }
+
+  @Override
+  public boolean next() {
+    return ThreadLocalRandom.current().nextDouble() < threshold;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java b/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java
new file mode 100644
index 0000000..af0165c
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/Sampler.java
@@ -0,0 +1,131 @@
+/*
+ * 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.core;
+
+import java.lang.reflect.Constructor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Extremely simple callback to determine the frequency that an action should be
+ * performed.
+ * <p/>
+ * For example, the next() function may look like this:
+ * <p/>
+ * <pre>
+ * <code>
+ * public boolean next() {
+ *   return Math.random() &gt; 0.5;
+ * }
+ * </code>
+ * </pre>
+ * This would trace 50% of all gets, 75% of all puts and would not trace any other requests.
+ */
+public abstract class Sampler {
+  /**
+   * A {@link Sampler} builder. It takes a {@link Sampler} class name and
+   * constructs an instance of that class, with the provided configuration.
+   */
+  public static class Builder {
+    private static final Log LOG = LogFactory.getLog(Builder.class);
+
+    private final static String DEFAULT_PACKAGE = "org.apache.htrace.core";
+    private final HTraceConfiguration conf;
+    private String className;
+    private ClassLoader classLoader = Builder.class.getClassLoader();
+
+    public Builder(HTraceConfiguration conf) {
+      this.conf = conf;
+      reset();
+    }
+
+    public Builder reset() {
+      this.className = null;
+      return this;
+    }
+
+    public Builder className(String className) {
+      this.className = className;
+      return this;
+    }
+
+    public Builder classLoader(ClassLoader classLoader) {
+      this.classLoader = classLoader;
+      return this;
+    }
+
+    private void throwError(String errorStr) {
+      LOG.error(errorStr);
+      throw new RuntimeException(errorStr);
+    }
+
+    private void throwError(String errorStr, Throwable e) {
+      LOG.error(errorStr, e);
+      throw new RuntimeException(errorStr, e);
+    }
+
+    public Sampler build() {
+      Sampler sampler = newSampler();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("Created new sampler of type " +
+            sampler.getClass().getName(), new Exception());
+      }
+      return sampler;
+    }
+
+    private Sampler newSampler() {
+      if (className == null || className.isEmpty()) {
+        throwError("No sampler class specified.");
+      }
+      String str = className;
+      if (!str.contains(".")) {
+        str = DEFAULT_PACKAGE + "." + str;
+      }
+      Class cls = null;
+      try {
+        cls = classLoader.loadClass(str);
+      } catch (ClassNotFoundException e) {
+        throwError("Cannot find Sampler class " + str);
+      }
+      Constructor<Sampler> ctor = null;
+      try {
+        ctor = cls.getConstructor(HTraceConfiguration.class);
+      } catch (NoSuchMethodException e) {
+        throwError("Cannot find a constructor for class " +
+            str + "which takes an HTraceConfiguration.");
+      }
+      Sampler sampler = null;
+      try {
+        LOG.debug("Creating new instance of " + str + "...");
+        sampler = ctor.newInstance(conf);
+      } catch (ReflectiveOperationException e) {
+        throwError("Reflection error when constructing " +
+            str + ".", e);
+      } catch (Throwable t) {
+        throwError("NewInstance error when constructing " +
+            str + ".", t);
+      }
+      return sampler;
+    }
+  }
+
+  public static final Sampler ALWAYS = AlwaysSampler.INSTANCE;
+  public static final Sampler NEVER = NeverSampler.INSTANCE;
+
+  public abstract boolean next();
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/Span.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Span.java b/htrace-core4/src/main/java/org/apache/htrace/core/Span.java
new file mode 100644
index 0000000..e63d414
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/Span.java
@@ -0,0 +1,193 @@
+/*
+ * 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.core;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+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;
+
+/**
+ * Base interface for gathering and reporting statistics about a block of
+ * execution.
+ * <p/>
+ * Spans should form a directed acyclic graph structure.  It should be possible
+ * to keep following the parents of a span until you arrive at a span with no
+ * parents.<p/>
+ */
+@JsonSerialize(using = Span.SpanSerializer.class)
+public interface Span {
+  /**
+   * The block has completed, stop the clock
+   */
+  void stop();
+
+  /**
+   * Get the start time, in milliseconds
+   */
+  long getStartTimeMillis();
+
+  /**
+   * Get the stop time, in milliseconds
+   */
+  long getStopTimeMillis();
+
+  /**
+   * Return the total amount of time elapsed since start was called, if running,
+   * or difference between stop and start
+   */
+  long getAccumulatedMillis();
+
+  /**
+   * Has the span been started and not yet stopped?
+   */
+  boolean isRunning();
+
+  /**
+   * Return a textual description of this span.<p/>
+   *
+   * Will never be null.
+   */
+  String getDescription();
+
+  /**
+   * A pseudo-unique (random) number assigned to this span instance.<p/>
+   *
+   * The spanId is immutable and cannot be changed.  It is safe to access this
+   * from multiple threads.
+   */
+  SpanId getSpanId();
+
+  /**
+   * Create a child span of this span with the given description
+   * @deprecated Since 4.0.0. Use {@link MilliSpan.Builder}
+   */
+  @Deprecated
+  Span child(String description);
+
+  @Override
+  String toString();
+
+  /**
+   * Returns the parent IDs of the span.<p/>
+   *
+   * The array will be empty if there are no parents.
+   */
+  SpanId[] getParents();
+
+  /**
+   * Set the parents of this span.<p/>
+   *
+   * Any existing parents will be cleared by this call.
+   */
+  void setParents(SpanId[] parents);
+
+  /**
+   * Add a data annotation associated with this span
+   */
+  void addKVAnnotation(String key, String value);
+
+  /**
+   * Add a timeline annotation associated with this span
+   */
+  void addTimelineAnnotation(String msg);
+
+  /**
+   * Get data associated with this span (read only)<p/>
+   *
+   * Will never be null.
+   */
+  Map<String, String> getKVAnnotations();
+
+  /**
+   * Get any timeline annotations (read only)<p/>
+   *
+   * Will never be null.
+   */
+  List<TimelineAnnotation> getTimelineAnnotations();
+
+  /**
+   * Return a unique id for the process from which this Span originated.<p/>
+   *
+   * Will never be null.
+   */
+  String getTracerId();
+
+  /**
+   * Set the process id of a span.
+   */
+  void setTracerId(String s);
+
+  /**
+   * 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();
+      if (span.getSpanId().isValid()) {
+        jgen.writeStringField("a", span.getSpanId().toString());
+      }
+      if (span.getStartTimeMillis() != 0) {
+        jgen.writeNumberField("b", span.getStartTimeMillis());
+      }
+      if (span.getStopTimeMillis() != 0) {
+        jgen.writeNumberField("e", span.getStopTimeMillis());
+      }
+      if (!span.getDescription().isEmpty()) {
+        jgen.writeStringField("d", span.getDescription());
+      }
+      String tracerId = span.getTracerId();
+      if (!tracerId.isEmpty()) {
+        jgen.writeStringField("r", tracerId);
+      }
+      jgen.writeArrayFieldStart("p");
+      for (SpanId parent : span.getParents()) {
+        jgen.writeString(parent.toString());
+      }
+      jgen.writeEndArray();
+      Map<String, String> traceInfoMap = span.getKVAnnotations();
+      if (!traceInfoMap.isEmpty()) {
+        jgen.writeObjectFieldStart("n");
+        for (Map.Entry<String, String> e : traceInfoMap.entrySet()) {
+          jgen.writeStringField(e.getKey(), e.getValue());
+        }
+        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();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java b/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java
new file mode 100644
index 0000000..ed31ad3
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/SpanId.java
@@ -0,0 +1,146 @@
+/*
+ * 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.core;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Uniquely identifies an HTrace span.
+ *
+ * Span IDs are 128 bits in total.  The upper 64 bits of a span ID is the same
+ * as the upper 64 bits of the parent span, if there is one.  The lower 64 bits
+ * are always random.
+ */
+public final class SpanId implements Comparable<SpanId> {
+  private static final int SPAN_ID_STRING_LENGTH = 32;
+  private final long high;
+  private final long low;
+
+  /**
+   * The invalid span ID, which is all zeroes.
+   *
+   * It is also the "least" span ID in the sense that it is considered
+   * smaller than any other span ID.
+   */
+  public static SpanId INVALID = new SpanId(0, 0);
+
+  private static long nonZeroRand64() {
+    while (true) {
+      long r = ThreadLocalRandom.current().nextLong();
+      if (r != 0) {
+        return r;
+      }
+    }
+  }
+
+  public static SpanId fromRandom() {
+    return new SpanId(nonZeroRand64(), nonZeroRand64());
+  }
+
+  public static SpanId fromString(String str) {
+    if (str.length() != SPAN_ID_STRING_LENGTH) {
+      throw new RuntimeException("Invalid SpanID string: length was not " +
+          SPAN_ID_STRING_LENGTH);
+    }
+    long high =
+      ((Long.parseLong(str.substring(0, 8), 16)) << 32) |
+      (Long.parseLong(str.substring(8, 16), 16));
+    long low =
+      ((Long.parseLong(str.substring(16, 24), 16)) << 32) |
+      (Long.parseLong(str.substring(24, 32), 16));
+    return new SpanId(high, low);
+  }
+
+  public SpanId(long high, long low) {
+    this.high = high;
+    this.low = low;
+  }
+
+  public long getHigh() {
+    return high;
+  }
+
+  public long getLow() {
+    return low;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof SpanId)) {
+      return false;
+    }
+    SpanId other = (SpanId)o;
+    return ((other.high == high) && (other.low == low));
+  }
+
+  @Override
+  public int compareTo(SpanId other) {
+    int cmp = compareAsUnsigned(high, other.high);
+    if (cmp != 0) {
+      return cmp;
+    }
+    return compareAsUnsigned(low, other.low);
+  }
+
+  private static int compareAsUnsigned(long a, long b) {
+    boolean aSign = a < 0;
+    boolean bSign = b < 0;
+    if (aSign != bSign) {
+      if (aSign) {
+        return 1;
+      } else {
+        return -1;
+      }
+    }
+    if (aSign) {
+      a = -a;
+      b = -b;
+    }
+    if (a < b) {
+      return -1;
+    } else if (a > b) {
+      return 1;
+    } else {
+      return 0;
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    return (int)((0xffffffff & (high >> 32))) ^
+           (int)((0xffffffff & (high >> 0))) ^
+           (int)((0xffffffff & (low >> 32))) ^
+           (int)((0xffffffff & (low >> 0)));
+  }
+
+  @Override
+  public String toString() {
+    return String.format("%08x%08x%08x%08x",
+        (0x00000000ffffffffL & (high >> 32)),
+        (0x00000000ffffffffL & high),
+        (0x00000000ffffffffL & (low >> 32)),
+        (0x00000000ffffffffL & low));
+  }
+
+  public boolean isValid() {
+    return (high != 0)  || (low != 0);
+  }
+
+  public SpanId newChildId() {
+    return new SpanId(high, nonZeroRand64());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java
new file mode 100644
index 0000000..a955ddf
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/SpanReceiver.java
@@ -0,0 +1,164 @@
+/*
+ * 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.core;
+
+import java.io.Closeable;
+import java.lang.reflect.Constructor;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * 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>
+ */
+public abstract class SpanReceiver implements Closeable {
+  /**
+   * A {@link SpanReceiver} builder. It takes a {@link SpanReceiver} class name
+   * and constructs an instance of that class, with the provided configuration.
+   */
+  public static class Builder {
+    private static final Log LOG = LogFactory.getLog(Builder.class);
+
+    private final static String DEFAULT_PACKAGE = "org.apache.htrace.core";
+    private final HTraceConfiguration conf;
+    private boolean logErrors;
+    private String className;
+    private ClassLoader classLoader = Builder.class.getClassLoader();
+
+    public Builder(HTraceConfiguration conf) {
+      this.conf = conf;
+      reset();
+    }
+
+    /**
+     * Set this builder back to defaults.
+     *
+     * @return this instance.
+     */
+    public Builder reset() {
+      this.logErrors = true;
+      this.className = null;
+      return this;
+    }
+
+    public Builder className(final String className) {
+      this.className = className;
+      return this;
+    }
+
+    /**
+     * Configure whether we should log errors during build().
+     * @return This instance
+     */
+    public Builder logErrors(boolean logErrors) {
+      this.logErrors = logErrors;
+      return this;
+    }
+
+    public Builder classLoader(ClassLoader classLoader) {
+      this.classLoader = classLoader;
+      return this;
+    }
+
+    private void throwError(String errorStr) {
+      if (logErrors) {
+        LOG.error(errorStr);
+      }
+      throw new RuntimeException(errorStr);
+    }
+
+    private void throwError(String errorStr, Throwable e) {
+      if (logErrors) {
+        LOG.error(errorStr, e);
+      }
+      throw new RuntimeException(errorStr, e);
+    }
+
+    public SpanReceiver build() {
+      SpanReceiver spanReceiver = newSpanReceiver();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("Created new span receiver of type " +
+                  spanReceiver.getClass().getName());
+      }
+      return spanReceiver;
+    }
+
+    private SpanReceiver newSpanReceiver() {
+      if ((className == null) || className.isEmpty()) {
+        throwError("No span receiver class specified.");
+      }
+      String str = className;
+      if (!str.contains(".")) {
+        str = DEFAULT_PACKAGE + "." + str;
+      }
+      Class cls = null;
+      try {
+        cls = classLoader.loadClass(str);
+      } catch (ClassNotFoundException e) {
+        throwError("Cannot find SpanReceiver class " + str);
+      }
+      Constructor<SpanReceiver> ctor = null;
+      try {
+        ctor = cls.getConstructor(HTraceConfiguration.class);
+      } catch (NoSuchMethodException e) {
+        throwError("Cannot find a constructor for class " +
+            str + "which takes an HTraceConfiguration.");
+      }
+      SpanReceiver receiver = null;
+      try {
+        LOG.debug("Creating new instance of " + str + "...");
+        receiver = ctor.newInstance(conf);
+      } catch (ReflectiveOperationException e) {
+        throwError("Reflection error when constructing " +
+            str + ".", e);
+      } catch (Throwable t) {
+        throwError("NewInstance error when constructing " +
+            str + ".", t);
+      }
+      return receiver;
+    }
+  }
+
+  /**
+   * An ID which uniquely identifies this SpanReceiver.
+   */
+  private final long id;
+
+  private static final AtomicLong HIGHEST_SPAN_RECEIVER_ID = new AtomicLong(0);
+
+  /**
+   * Get an ID uniquely identifying this SpanReceiver.
+   */
+  public final long getId() {
+    return id;
+  }
+
+  protected SpanReceiver() {
+    this.id = HIGHEST_SPAN_RECEIVER_ID.incrementAndGet();
+  }
+
+  /**
+   * Called when a Span is stopped and can now be stored.
+   */
+  public abstract void receiveSpan(Span span);
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java b/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
new file mode 100644
index 0000000..f443ec6
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/StandardOutSpanReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * 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.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+
+/**
+ * Used for testing. Simply prints to standard out any spans it receives.
+ */
+public class StandardOutSpanReceiver extends SpanReceiver {
+  private static final Log LOG = LogFactory.getLog(StandardOutSpanReceiver.class);
+
+  public StandardOutSpanReceiver(HTraceConfiguration conf) {
+    LOG.trace("Created new StandardOutSpanReceiver.");
+  }
+
+  @Override
+  public void receiveSpan(Span span) {
+    System.out.println(span);
+  }
+
+  @Override
+  public void close() throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java b/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
new file mode 100644
index 0000000..18de061
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TimelineAnnotation.java
@@ -0,0 +1,40 @@
+/*
+ * 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.core;
+
+public class TimelineAnnotation {
+  private final long time;
+  private final String msg;
+
+  public TimelineAnnotation(long time, String msg) {
+    this.time = time;
+    this.msg = msg;
+  }
+
+  public long getTime() {
+    return time;
+  }
+
+  public String getMessage() {
+    return msg;
+  }
+
+  @Override
+  public String toString() {
+    return "@" + time + ": " + msg;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java
new file mode 100644
index 0000000..a0fec17
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceCallable.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Wrap a Callable with a Span that survives a change in threads.
+ */
+public class TraceCallable<V> implements Callable<V> {
+  private final Tracer tracer;
+  private final Callable<V> impl;
+  private final TraceScope parent;
+  private final String description;
+
+  TraceCallable(Tracer tracer, TraceScope parent, Callable<V> impl,
+      String description) {
+    this.tracer = tracer;
+    this.impl = impl;
+    this.parent = parent;
+    if (description == null) {
+      this.description = Thread.currentThread().getName();
+    } else {
+      this.description = description;
+    }
+  }
+
+  @Override
+  public V call() throws Exception {
+    TraceScope chunk = tracer.newScope(description,
+        parent.getSpan().getSpanId());
+    try {
+      return impl.call();
+    } finally {
+      chunk.close();
+    }
+  }
+
+  public Callable<V> getImpl() {
+    return impl;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java
new file mode 100644
index 0000000..81e31ea
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceExecutorService.java
@@ -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 org.apache.htrace.core;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class TraceExecutorService implements ExecutorService {
+  private final Tracer tracer;
+  private final String scopeName;
+  private final ExecutorService impl;
+
+  TraceExecutorService(Tracer tracer, String scopeName,
+                       ExecutorService impl) {
+    this.tracer = tracer;
+    this.scopeName = scopeName;
+    this.impl = impl;
+  }
+
+  @Override
+  public void execute(Runnable command) {
+    impl.execute(tracer.wrap(command, scopeName));
+  }
+
+  @Override
+  public void shutdown() {
+    impl.shutdown();
+  }
+
+  @Override
+  public List<Runnable> shutdownNow() {
+    return impl.shutdownNow();
+  }
+
+  @Override
+  public boolean isShutdown() {
+    return impl.isShutdown();
+  }
+
+  @Override
+  public boolean isTerminated() {
+    return impl.isTerminated();
+  }
+
+  @Override
+  public boolean awaitTermination(long timeout, TimeUnit unit)
+      throws InterruptedException {
+    return impl.awaitTermination(timeout, unit);
+  }
+
+  @Override
+  public <T> Future<T> submit(Callable<T> task) {
+    return impl.submit(tracer.wrap(task, scopeName));
+  }
+
+  @Override
+  public <T> Future<T> submit(Runnable task, T result) {
+    return impl.submit(tracer.wrap(task, scopeName), result);
+  }
+
+  @Override
+  public Future<?> submit(Runnable task) {
+    return impl.submit(tracer.wrap(task, scopeName));
+  }
+
+  private <T> Collection<? extends Callable<T>> wrapCollection(
+      Collection<? extends Callable<T>> tasks) {
+    List<Callable<T>> result = new ArrayList<Callable<T>>();
+    for (Callable<T> task : tasks) {
+      result.add(tracer.wrap(task, scopeName));
+    }
+    return result;
+  }
+
+  @Override
+  public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+      throws InterruptedException {
+    return impl.invokeAll(wrapCollection(tasks));
+  }
+
+  @Override
+  public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
+                                       long timeout, TimeUnit unit) throws InterruptedException {
+    return impl.invokeAll(wrapCollection(tasks), timeout, unit);
+  }
+
+  @Override
+  public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+      throws InterruptedException, ExecutionException {
+    return impl.invokeAny(wrapCollection(tasks));
+  }
+
+  @Override
+  public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout,
+                         TimeUnit unit) throws InterruptedException, ExecutionException,
+      TimeoutException {
+    return impl.invokeAny(wrapCollection(tasks), timeout, unit);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java
new file mode 100644
index 0000000..8f98708
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceRunnable.java
@@ -0,0 +1,54 @@
+/*
+ * 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.core;
+
+/**
+ * Wrap a Runnable with a Span that survives a change in threads.
+ */
+public class TraceRunnable implements Runnable {
+  private final Tracer tracer;
+  private final TraceScope parent;
+  private final Runnable runnable;
+  private final String description;
+
+  public TraceRunnable(Tracer tracer, TraceScope parent,
+      Runnable runnable, String description) {
+    this.tracer = tracer;
+    this.parent = parent;
+    this.runnable = runnable;
+    if (description == null) {
+      this.description = Thread.currentThread().getName();
+    } else {
+      this.description = description;
+    }
+  }
+
+  @Override
+  public void run() {
+    TraceScope chunk = tracer.newScope(description,
+        parent.getSpan().getSpanId());
+    try {
+      runnable.run();
+    } finally {
+      chunk.close();
+    }
+  }
+
+  public Runnable getRunnable() {
+    return runnable;
+  }
+}



[2/5] incubator-htrace git commit: HTRACE-259. Rename htrace-core module to htrace-core4 to match the artifactId (stack via cmccabe)

Posted by cm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java b/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java
new file mode 100644
index 0000000..05a053e
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TraceScope.java
@@ -0,0 +1,128 @@
+/*
+ * 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.core;
+
+import java.io.Closeable;
+
+/**
+ * Create a new TraceScope at major transitions. Hosts current tracing context.
+ */
+public class TraceScope implements Closeable {
+  /**
+   * The tracer to use for this scope.
+   */
+  final Tracer tracer;
+
+  /**
+   * The trace span for this scope, or null if the scope is closed.
+   *
+   * If the scope is closed, it must also be detached.
+   */
+  private final Span span;
+
+  /**
+   * The parent of this trace scope, or null if there is no parent.
+   */
+  private TraceScope parent;
+
+  /**
+   * True if this scope is detached.
+   */
+  boolean detached;
+
+  TraceScope(Tracer tracer, Span span, TraceScope parent) {
+    this.tracer = tracer;
+    this.span = span;
+    this.parent = parent;
+    this.detached = false;
+  }
+
+  /**
+   * Returns the span which this scope is managing.
+   */
+  public Span getSpan() {
+    return span;
+  }
+
+  /**
+   * Returns the span ID which this scope is managing.
+   */
+  public SpanId getSpanId() {
+    return span.getSpanId();
+  }
+
+  TraceScope getParent() {
+    return parent;
+  }
+
+  void setParent(TraceScope parent) {
+    this.parent = parent;
+  }
+
+  /**
+   * Detach this TraceScope from the current thread.
+   *
+   * It is OK to "leak" TraceScopes which have been detached.  They will not
+   * consume any resources other than a small amount of memory until they are
+   * garbage collected.  On the other hand, trace scopes which are still
+   * attached must never be leaked.
+   */
+  public void detach() {
+    if (detached) {
+      Tracer.throwClientError("Can't detach this TraceScope  because " +
+          "it is already detached.");
+    }
+    tracer.detachScope(this);
+    detached = true;
+    parent = null;
+  }
+
+  /**
+   * Attach this TraceScope to the current thread.
+   */
+  public void reattach() {
+    if (!detached) {
+      Tracer.throwClientError("Can't reattach this TraceScope  because " +
+          "it is not detached.");
+    }
+    tracer.reattachScope(this);
+    detached = false;
+  }
+
+  /**
+   * Close this TraceScope, ending the trace span it is managing.
+   */
+  @Override
+  public void close() {
+    tracer.closeScope(this);
+  }
+
+  public void addKVAnnotation(String key, String value) {
+    span.addKVAnnotation(key, value);
+  }
+
+  public void addTimelineAnnotation(String msg) {
+    span.addTimelineAnnotation(msg);
+  }
+
+  @Override
+  public String toString() {
+    return "TraceScope(tracerId=" + tracer.getTracerId() +
+        ", span=" + span.toJson() +
+        ", detached=" + detached + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java b/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java
new file mode 100644
index 0000000..39d972d
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/Tracer.java
@@ -0,0 +1,673 @@
+/*
+ * 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.core;
+
+import java.io.Closeable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Use a Tracer instance inside a 'process' to collect and distribute its trace Spans.
+ * Example processes are an HDFS DataNode or an HBase RegionServer. A Tracer instance is your
+ * one-stop shop for all things tracing.
+ * 
+ * <p>
+ */
+public class Tracer implements Closeable {
+  private static final Log LOG = LogFactory.getLog(Tracer.class);
+
+  public final static String SPAN_RECEIVER_CLASSES_KEY = "span.receiver.classes";
+  public final static String SAMPLER_CLASSES_KEY = "sampler.classes";
+
+  public static class Builder {
+    private String name;
+    private HTraceConfiguration conf = HTraceConfiguration.EMPTY;
+    private ClassLoader classLoader =
+        Builder.class.getClassLoader();
+    private TracerPool tracerPool = TracerPool.GLOBAL;
+
+    /**
+     * @deprecated Since 4.0.0. Use Constructor that takes a <code>name</code> argument instead
+     */
+    @Deprecated
+    public Builder() {
+    }
+
+    public Builder(final String name) {
+      name(name);
+    }
+
+    /**
+     * @param name
+     * @return This
+     * @deprecated Since 4.0.0. Use Constructor that takes a <code>name</code> argument instead.
+     */
+    @Deprecated
+    public Builder name(String name) {
+      this.name = name;
+      return this;
+    }
+
+    public Builder conf(HTraceConfiguration conf) {
+      this.conf = conf;
+      return this;
+    }
+
+    public Builder tracerPool(TracerPool tracerPool) {
+      this.tracerPool = tracerPool;
+      return this;
+    }
+
+    private void loadSamplers(List<Sampler> samplers) {
+      String classNamesStr = conf.get(SAMPLER_CLASSES_KEY, "");
+      List<String> classNames = getClassNamesFromConf(classNamesStr);
+      StringBuilder bld = new StringBuilder();
+      String prefix = "";
+      for (String className : classNames) {
+        try {
+          Sampler sampler = new Sampler.Builder(conf).
+            className(className).
+            classLoader(classLoader).
+            build();
+          samplers.add(sampler);
+          bld.append(prefix).append(className);
+          prefix = ", ";
+        } catch (Throwable e) {
+          LOG.error("Failed to create SpanReceiver of type " + className, e);
+        }
+      }
+      String resultString = bld.toString();
+      if (resultString.isEmpty()) {
+        resultString = "no samplers";
+      }
+      LOG.debug(SAMPLER_CLASSES_KEY + " = " + classNamesStr +
+          "; loaded " + resultString);
+    }
+
+    private void loadSpanReceivers() {
+      String classNamesStr = conf.get(SPAN_RECEIVER_CLASSES_KEY, "");
+      List<String> classNames = getClassNamesFromConf(classNamesStr);
+      StringBuilder bld = new StringBuilder();
+      String prefix = "";
+      for (String className : classNames) {
+        try {
+          tracerPool.loadReceiverType(className, conf, classLoader);
+          bld.append(prefix).append(className);
+          prefix = ", ";
+        } catch (Throwable e) {
+          LOG.error("Failed to create SpanReceiver of type " + className, e);
+        }
+      }
+      String resultString = bld.toString();
+      if (resultString.isEmpty()) {
+        resultString = "no span receivers";
+      }
+      LOG.debug(SPAN_RECEIVER_CLASSES_KEY + " = " + classNamesStr +
+          "; loaded " + resultString);
+    }
+
+    /**
+     * Get a list of class names from the HTrace configuration.
+     * Entries which are empty will be removed.  Entries which lack a package will
+     * be given the default package.
+     *
+     * @param classNamesStr     A semicolon-separated string containing a list
+     *                            of class names.
+     * @return                  A list of class names.
+     */
+    private List<String> getClassNamesFromConf(String classNamesStr) {
+      String classNames[] = classNamesStr.split(";");
+      LinkedList<String> cleanedClassNames = new LinkedList<String>();
+      for (String className : classNames) {
+        String cleanedClassName = className.trim();
+        if (!cleanedClassName.isEmpty()) {
+          cleanedClassNames.add(cleanedClassName);
+        }
+      }
+      return cleanedClassNames;
+    }
+
+    public Tracer build() {
+      if (name == null) {
+        throw new RuntimeException("You must specify a name for this Tracer.");
+      }
+      LinkedList<Sampler> samplers = new LinkedList<Sampler>();
+      loadSamplers(samplers);
+      String tracerId = new TracerId(conf, name).get();
+      Tracer tracer = new Tracer(tracerId, tracerPool,
+          samplers.toArray(new Sampler[samplers.size()]));
+      tracerPool.addTracer(tracer);
+      loadSpanReceivers();
+      if (LOG.isTraceEnabled()) {
+        LOG.trace("Created " + tracer + " for " + name);
+      }
+      return tracer;
+    }
+  }
+
+  /**
+   * The thread-specific context for this Tracer.
+   *
+   * This tracks the current number of trace scopes in a particular thread
+   * created by this tracer.  We use this to apply our samplers only for the
+   * "top-level" spans.
+   *
+   * Note that we can't put the TraceScope objects themselves in this context,
+   * since we need to be able to use TraceScopes created by other Tracers, and
+   * this context is per-Tracer.
+   */
+  private static class ThreadContext {
+    private long depth;
+
+    ThreadContext() {
+      this.depth = 0;
+    }
+
+    boolean isTopLevel() {
+      return (depth == 0);
+    }
+
+    void pushScope() {
+      depth++;
+    }
+
+    TraceScope pushNewScope(Tracer tracer, Span span, TraceScope parentScope) {
+      TraceScope scope = new TraceScope(tracer, span, parentScope);
+      threadLocalScope.set(scope);
+      depth++;
+      return scope;
+    }
+
+    void popScope() {
+      if (depth <= 0) {
+        throwClientError("There were more trace scopes closed than " +
+            "were opened.");
+      }
+      depth--;
+    }
+  };
+
+  /**
+   * A subclass of ThreadLocal that starts off with a non-null initial value in
+   * each thread.
+   */
+  private static class ThreadLocalContext extends ThreadLocal<ThreadContext> {
+    @Override
+    protected ThreadContext initialValue() {
+      return new ThreadContext();
+    }
+  };
+
+  /**
+   * The current trace scope.  This is global, so it is shared amongst all
+   * libraries using HTrace.
+   */
+  final static ThreadLocal<TraceScope> threadLocalScope =
+      new ThreadLocal<TraceScope>();
+
+  /**
+   * An empty array of SpanId objects.  Can be used rather than constructing a
+   * new object whenever we need an empty array.
+   */
+  private static final SpanId EMPTY_PARENT_ARRAY[] = new SpanId[0];
+
+  /**
+   * The tracerId.
+   */
+  private final String tracerId;
+
+  /**
+   * The TracerPool which this Tracer belongs to.
+   *
+   * This gets set to null after the Tracer is closed in order to catch some
+   * use-after-close errors.  Note that we do not synchronize access on this
+   * field, since it only changes when the Tracer is closed, and the Tracer
+   * should not be used after that.
+   */
+  private TracerPool tracerPool;
+
+  /**
+   * The current thread-local context for this particualr Tracer.
+   */
+  private final ThreadLocalContext threadContext;
+
+  /**
+   * The NullScope instance for this Tracer.
+   */
+  private final NullScope nullScope;
+
+  /**
+   * The currently active Samplers.
+   *
+   * Arrays are immutable once set.  You must take the Tracer lock in order to
+   * set this to a new array.  If this is null, the Tracer is closed.
+   */
+  private volatile Sampler[] curSamplers;
+
+  /**
+   * Log a client error, and throw an exception.
+   *
+   * @param str     The message to use in the log and the exception.
+   */
+  static void throwClientError(String str) {
+    LOG.error(str);
+    throw new RuntimeException(str);
+  }
+
+  /**
+   * @return If the current thread is tracing, this function returns the Tracer that is
+   * being used; otherwise, it returns null.
+   */
+  public static Tracer curThreadTracer() {
+    TraceScope traceScope = threadLocalScope.get();
+    if (traceScope == null) {
+      return null;
+    }
+    return traceScope.tracer;
+  }
+
+  Tracer(String tracerId, TracerPool tracerPool, Sampler[] curSamplers) {
+    this.tracerId = tracerId;
+    this.tracerPool = tracerPool;
+    this.threadContext = new ThreadLocalContext();
+    this.nullScope = new NullScope(this);
+    this.curSamplers = curSamplers;
+  }
+
+  public String getTracerId() {
+    return tracerId;
+  }
+
+  private TraceScope newScopeImpl(ThreadContext context, String description) {
+    Span span = new MilliSpan.Builder().
+        tracerId(tracerId).
+        begin(System.currentTimeMillis()).
+        description(description).
+        parents(EMPTY_PARENT_ARRAY).
+        spanId(SpanId.fromRandom()).
+        build();
+    return context.pushNewScope(this, span, null);
+  }
+
+  private TraceScope newScopeImpl(ThreadContext context, String description,
+        TraceScope parentScope) {
+    SpanId parentId = parentScope.getSpan().getSpanId();
+    Span span = new MilliSpan.Builder().
+        tracerId(tracerId).
+        begin(System.currentTimeMillis()).
+        description(description).
+        parents(new SpanId[] { parentId }).
+        spanId(parentId.newChildId()).
+        build();
+    return context.pushNewScope(this, span, parentScope);
+  }
+
+  private TraceScope newScopeImpl(ThreadContext context, String description,
+        SpanId parentId) {
+    Span span = new MilliSpan.Builder().
+        tracerId(tracerId).
+        begin(System.currentTimeMillis()).
+        description(description).
+        parents(new SpanId[] { parentId }).
+        spanId(parentId.newChildId()).
+        build();
+    return context.pushNewScope(this, span, null);
+  }
+
+  private TraceScope newScopeImpl(ThreadContext context, String description,
+        TraceScope parentScope, SpanId secondParentId) {
+    SpanId parentId = parentScope.getSpan().getSpanId();
+    Span span = new MilliSpan.Builder().
+        tracerId(tracerId).
+        begin(System.currentTimeMillis()).
+        description(description).
+        parents(new SpanId[] { parentId, secondParentId }).
+        spanId(parentId.newChildId()).
+        build();
+    return context.pushNewScope(this, span, parentScope);
+  }
+
+  /**
+   * Create a new trace scope.
+   *
+   * If there are no scopes above the current scope, we will apply our
+   * configured samplers. Otherwise, we will create a trace Span only if this thread
+   * is already tracing, or if the passed parentID was valid.
+   *
+   * @param description         The description of the new span to create.
+   * @param parentId            If this is a valid span ID, it will be added to
+   *                              the parents of the new span we create.
+   * @return                    The new trace scope.
+   */
+  public TraceScope newScope(String description, SpanId parentId) {
+    TraceScope parentScope = threadLocalScope.get();
+    ThreadContext context = threadContext.get();
+    if (parentScope != null) {
+      if (parentId.isValid() &&
+          (!parentId.equals(parentScope.getSpan().getSpanId()))) {
+        return newScopeImpl(context, description, parentScope, parentId);
+      } else {
+        return newScopeImpl(context, description, parentScope);
+      }
+    } else if (parentId.isValid()) {
+      return newScopeImpl(context, description, parentId);
+    }
+    if (!context.isTopLevel()) {
+      context.pushScope();
+      return nullScope;
+    }
+    if (!sample()) {
+      context.pushScope();
+      return nullScope;
+    }
+    return newScopeImpl(context, description);
+  }
+
+  /**
+   * Create a new trace scope.
+   *
+   * If there are no scopes above the current scope, we will apply our
+   * configured samplers. Otherwise, we will create a trace Span only if this thread
+   * is already tracing.
+   * @param description         The description of the new span to create.
+   * @return                    The new trace scope.
+   */
+  public TraceScope newScope(String description) {
+    TraceScope parentScope = threadLocalScope.get();
+    ThreadContext context = threadContext.get();
+    if (parentScope != null) {
+      return newScopeImpl(context, description, parentScope);
+    }
+    if (!context.isTopLevel()) {
+      context.pushScope();
+      return nullScope;
+    }
+    if (!sample()) {
+      context.pushScope();
+      return nullScope;
+    }
+    return newScopeImpl(context, description);
+  }
+
+  /**
+   * Return a null trace scope.
+   */
+  public TraceScope newNullScope() {
+    ThreadContext context = threadContext.get();
+    context.pushScope();
+    return nullScope;
+  }
+
+  /**
+   * Wrap the callable in a TraceCallable, if tracing.
+   *
+   * @return The callable provided, wrapped if tracing, 'callable' if not.
+   */
+  public <V> Callable<V> wrap(Callable<V> callable, String description) {
+    TraceScope parentScope = threadLocalScope.get();
+    if (parentScope == null) {
+      return callable;
+    }
+    return new TraceCallable<V>(this, parentScope, callable, description);
+  }
+
+  /**
+   * Wrap the runnable in a TraceRunnable, if tracing
+   *
+   * @return The runnable provided, wrapped if tracing, 'runnable' if not.
+   */
+  public Runnable wrap(Runnable runnable, String description) {
+    TraceScope parentScope = threadLocalScope.get();
+    if (parentScope == null) {
+      return runnable;
+    }
+    return new TraceRunnable(this, parentScope, runnable, description);
+  }
+
+  public TraceExecutorService newTraceExecutorService(ExecutorService impl,
+                                                      String scopeName) {
+    return new TraceExecutorService(this, scopeName, impl);
+  }
+
+  public TracerPool getTracerPool() {
+    if (tracerPool == null) {
+      throwClientError(toString() + " is closed.");
+    }
+    return tracerPool;
+  }
+
+  /**
+   * Returns an object that will trace all calls to itself.
+   */
+  @SuppressWarnings("unchecked")
+  <T, V> T createProxy(final T instance) {
+    final Tracer tracer = this;
+    InvocationHandler handler = new InvocationHandler() {
+      @Override
+      public Object invoke(Object obj, Method method, Object[] args)
+          throws Throwable {
+        TraceScope scope = tracer.newScope(method.getName());
+        try {
+          return method.invoke(instance, args);
+        } catch (Throwable ex) {
+          ex.printStackTrace();
+          throw ex;
+        } finally {
+          scope.close();
+        }
+      }
+    };
+    return (T) Proxy.newProxyInstance(instance.getClass().getClassLoader(),
+        instance.getClass().getInterfaces(), handler);
+  }
+
+  /**
+   * Return true if we should create a new top-level span.
+   *
+   * We will create the span if any configured sampler returns true.
+   */
+  private boolean sample() {
+    Sampler[] samplers = curSamplers;
+    for (Sampler sampler : samplers) {
+      if (sampler.next()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns an array of all the current Samplers.
+   *
+   * Note that if the current Samplers change, those changes will not be
+   * reflected in this array.  In other words, this array may be stale.
+   */
+  public Sampler[] getSamplers() {
+    return curSamplers;
+  }
+
+  /**
+   * Add a new Sampler.
+   *
+   * @param sampler       The new sampler to add.
+   *                      You cannot add a particular Sampler object more
+   *                        than once.  You may add multiple Sampler objects
+   *                        of the same type, although this is not recommended.
+   *
+   * @return              True if the sampler was added; false if it already had
+   *                        been added earlier.
+   */
+  public synchronized boolean addSampler(Sampler sampler) {
+    if (tracerPool == null) {
+      throwClientError(toString() + " is closed.");
+    }
+    Sampler[] samplers = curSamplers;
+    for (int i = 0; i < samplers.length; i++) {
+      if (samplers[i] == sampler) {
+        return false;
+      }
+    }
+    Sampler[] newSamplers =
+        Arrays.copyOf(samplers, samplers.length + 1);
+    newSamplers[samplers.length] = sampler;
+    curSamplers = newSamplers;
+    return true;
+  }
+
+  /**
+   * Remove a SpanReceiver.
+   *
+   * @param sampler       The sampler to remove.
+   */
+  public synchronized boolean removeSampler(Sampler sampler) {
+    if (tracerPool == null) {
+      throwClientError(toString() + " is closed.");
+    }
+    Sampler[] samplers = curSamplers;
+    for (int i = 0; i < samplers.length; i++) {
+      if (samplers[i] == sampler) {
+        Sampler[] newSamplers = new Sampler[samplers.length - 1];
+        System.arraycopy(samplers, 0, newSamplers, 0, i);
+        System.arraycopy(samplers, i + 1, newSamplers, i,
+            samplers.length - i - 1);
+        curSamplers = newSamplers;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  void detachScope(TraceScope scope) {
+    TraceScope curScope = threadLocalScope.get();
+    if (curScope != scope) {
+      throwClientError("Can't detach TraceScope for " +
+          scope.getSpan().toJson() + " because it is not the current " +
+          "TraceScope in thread " + Thread.currentThread().getName());
+    }
+    ThreadContext context = threadContext.get();
+    context.popScope();
+    threadLocalScope.set(scope.getParent());
+  }
+
+  void reattachScope(TraceScope scope) {
+    TraceScope parent = threadLocalScope.get();
+    Tracer.threadLocalScope.set(scope);
+    ThreadContext context = threadContext.get();
+    context.pushScope();
+    scope.setParent(parent);
+  }
+
+  void closeScope(TraceScope scope) {
+    TraceScope curScope = threadLocalScope.get();
+    if (curScope != scope) {
+      throwClientError("Can't close TraceScope for " +
+          scope.getSpan().toJson() + " because it is not the current " +
+          "TraceScope in thread " + Thread.currentThread().getName());
+    }
+    if (tracerPool == null) {
+      throwClientError(toString() + " is closed.");
+    }
+    SpanReceiver[] receivers = tracerPool.getReceivers();
+    if (receivers == null) {
+      throwClientError(toString() + " is closed.");
+    }
+    ThreadContext context = threadContext.get();
+    context.popScope();
+    threadLocalScope.set(scope.getParent());
+    scope.setParent(null);
+    Span span = scope.getSpan();
+    span.stop();
+    for (SpanReceiver receiver : receivers) {
+      receiver.receiveSpan(span);
+    }
+  }
+
+  void popNullScope() {
+    TraceScope curScope = threadLocalScope.get();
+    if (curScope != null) {
+      throwClientError("Attempted to close an empty scope, but it was not " +
+          "the current thread scope in thread " +
+          Thread.currentThread().getName());
+    }
+    ThreadContext context = threadContext.get();
+    context.popScope();
+  }
+
+  public static Span getCurrentSpan() {
+    TraceScope curScope = threadLocalScope.get();
+    if (curScope == null) {
+      return null;
+    } else {
+      return curScope.getSpan();
+    }
+  }
+
+  public static SpanId getCurrentSpanId() {
+    TraceScope curScope = threadLocalScope.get();
+    if (curScope == null) {
+      return SpanId.INVALID;
+    } else {
+      return curScope.getSpan().getSpanId();
+    }
+  }
+
+  @Override
+  public synchronized void close() {
+    if (tracerPool == null) {
+      return;
+    }
+    curSamplers = new Sampler[0];
+    tracerPool.removeTracer(this);
+  }
+
+  /**
+   * Get the hash code of a Tracer object.
+   *
+   * This hash code is based on object identity.
+   * This is used in TracerPool to create a hash table of Tracers.
+   */
+  @Override
+  public int hashCode() {
+    return System.identityHashCode(this);
+  }
+
+  /**
+   * Compare two tracer objects.
+   *
+   * Tracer objects are always compared by object equality.
+   * This is used in TracerPool to create a hash table of Tracers.
+   */
+  @Override
+  public boolean equals(Object other) {
+    return (this == other);
+  }
+
+  @Override
+  public String toString() {
+    return "Tracer(" + tracerId + ")";
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java b/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java
new file mode 100644
index 0000000..da482fe
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TracerId.java
@@ -0,0 +1,294 @@
+/*
+ * 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.core;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.TreeSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The HTrace tracer ID.<p/>
+ *
+ * HTrace tracer IDs are created from format strings.
+ * Format strings contain variables which the TracerId class will
+ * replace with the correct values at runtime.<p/>
+ *
+ * <ul>
+ * <li>%{tname}: the tracer name supplied when creating the Tracer.</li>
+ * <li>%{pname}: the process name obtained from the JVM.</li>
+ * <li>%{ip}: will be replaced with an ip address.</li>
+ * <li>%{pid}: the numerical process ID from the operating system.</li>
+ * </ul><p/>
+ *
+ * For example, the string "%{pname}/%{ip}" will be replaced with something
+ * like: DataNode/192.168.0.1, assuming that the process' name is DataNode
+ * and its IP address is 192.168.0.1.<p/>
+ *
+ *  ID strings can contain backslashes as escapes.
+ * For example, "\a" will map to "a".  "\%{ip}" will map to the literal
+ * string "%{ip}", not the IP address.  A backslash itself can be escaped by a
+ * preceding backslash.
+ */
+public final class TracerId {
+  private static final Log LOG = LogFactory.getLog(TracerId.class);
+
+  /**
+   * The configuration key to use for process id
+   */
+  public static final String TRACER_ID_KEY = "tracer.id";
+
+  /**
+   * The default tracer ID to use if no other ID is configured.
+   */
+  private static final String DEFAULT_TRACER_ID = "%{tname}/%{ip}";
+
+  private final String tracerName;
+
+  private final String tracerId;
+
+  public TracerId(HTraceConfiguration conf, String tracerName) {
+    this.tracerName = tracerName;
+    String fmt = conf.get(TRACER_ID_KEY, DEFAULT_TRACER_ID);
+    StringBuilder bld = new StringBuilder();
+    StringBuilder varBld = null;
+    boolean escaping = false;
+    int varSeen = 0;
+    for (int i = 0, len = fmt.length() ; i < len; i++) {
+      char c = fmt.charAt(i);
+      if (c == '\\') {
+        if (!escaping) {
+          escaping = true;
+          continue;
+        }
+      }
+      switch (varSeen) {
+        case 0:
+          if (c == '%') {
+            if (!escaping) {
+              varSeen = 1;
+              continue;
+            }
+          }
+          escaping = false;
+          varSeen = 0;
+          bld.append(c);
+          break;
+        case 1:
+          if (c == '{') {
+            if (!escaping) {
+              varSeen = 2;
+              varBld = new StringBuilder();
+              continue;
+            }
+          }
+          escaping = false;
+          varSeen = 0;
+          bld.append("%").append(c);
+          break;
+        default:
+          if (c == '}') {
+            if (!escaping) {
+              String var = varBld.toString();
+              bld.append(processShellVar(var));
+              varBld = null;
+              varSeen = 0;
+              continue;
+            }
+          }
+          escaping = false;
+          varBld.append(c);
+          varSeen++;
+          break;
+      }
+    }
+    if (varSeen > 0) {
+      LOG.warn("Unterminated process ID substitution variable at the end " +
+          "of format string " + fmt);
+    }
+    this.tracerId = bld.toString();
+    if (LOG.isTraceEnabled()) {
+      LOG.trace("ProcessID(fmt=" + fmt + "): computed process ID of \"" +
+          this.tracerId + "\"");
+    }
+  }
+
+  private String processShellVar(String var) {
+    if (var.equals("tname")) {
+      return tracerName;
+    } else if (var.equals("pname")) {
+      return getProcessName();
+    } else if (var.equals("ip")) {
+      return getBestIpString();
+    } else if (var.equals("pid")) {
+      return Long.valueOf(getOsPid()).toString();
+    } else {
+      LOG.warn("unknown ProcessID variable " + var);
+      return "";
+    }
+  }
+
+  static String getProcessName() {
+    String cmdLine = System.getProperty("sun.java.command");
+    if (cmdLine != null && !cmdLine.isEmpty()) {
+      String fullClassName = cmdLine.split("\\s+")[0];
+      String[] classParts = fullClassName.split("\\.");
+      cmdLine = classParts[classParts.length - 1];
+    }
+    return (cmdLine == null || cmdLine.isEmpty()) ? "Unknown" : cmdLine;
+  }
+
+  /**
+   * Get the best IP address that represents this node.<p/>
+   *
+   * This is complicated since nodes can have multiple network interfaces,
+   * and each network interface can have multiple IP addresses.  What we're
+   * looking for here is an IP address that will serve to identify this node
+   * to HTrace.  So we prefer site-local addresess (i.e. private ones on the
+   * LAN) to publicly routable interfaces.  If there are multiple addresses
+   * to choose from, we select the one which comes first in textual sort
+   * order.  This should ensure that we at least consistently call each node
+   * by a single name.
+   */
+  static String getBestIpString() {
+    Enumeration<NetworkInterface> ifaces;
+    try {
+      ifaces = NetworkInterface.getNetworkInterfaces();
+    } catch (SocketException e) {
+      LOG.error("Error getting network interfaces", e);
+      return "127.0.0.1";
+    }
+    TreeSet<String> siteLocalCandidates = new TreeSet<String>();
+    TreeSet<String> candidates = new TreeSet<String>();
+    while (ifaces.hasMoreElements()) {
+      NetworkInterface iface = ifaces.nextElement();
+      for (Enumeration<InetAddress> addrs =
+               iface.getInetAddresses(); addrs.hasMoreElements();) {
+        InetAddress addr = addrs.nextElement();
+        if (!addr.isLoopbackAddress()) {
+          if (addr.isSiteLocalAddress()) {
+            siteLocalCandidates.add(addr.getHostAddress());
+          } else {
+            candidates.add(addr.getHostAddress());
+          }
+        }
+      }
+    }
+    if (!siteLocalCandidates.isEmpty()) {
+      return siteLocalCandidates.first();
+    }
+    if (!candidates.isEmpty()) {
+      return candidates.first();
+    }
+    return "127.0.0.1";
+  }
+
+  /**
+   * Get the process id from the operating system.<p/>
+   *
+   * Unfortunately, there is no simple method to get the process id in Java.
+   * The approach we take here is to use the shell method (see
+   * {TracerId#getOsPidFromShellPpid}) unless we are on Windows, where the
+   * shell is not available.  On Windows, we use
+   * {TracerId#getOsPidFromManagementFactory}, which depends on some
+   * undocumented features of the JVM, but which doesn't require a shell.
+   */
+  static long getOsPid() {
+    if ((System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH)).
+        contains("windows")) {
+      return getOsPidFromManagementFactory();
+    } else {
+      return getOsPidFromShellPpid();
+    }
+  }
+
+  /**
+   * Get the process ID by executing a shell and printing the PPID (parent
+   * process ID).<p/>
+   *
+   * This method of getting the process ID doesn't depend on any undocumented
+   * features of the virtual machine, and should work on almost any UNIX
+   * operating system.
+   */
+  private static long getOsPidFromShellPpid() {
+    Process p = null;
+    StringBuilder sb = new StringBuilder();
+    try {
+      p = new ProcessBuilder("/usr/bin/env", "sh", "-c", "echo $PPID").
+        redirectErrorStream(true).start();
+      BufferedReader reader = new BufferedReader(
+          new InputStreamReader(p.getInputStream()));
+      String line = "";
+      while ((line = reader.readLine()) != null) {
+        sb.append(line.trim());
+      }
+      int exitVal = p.waitFor();
+      if (exitVal != 0) {
+        throw new IOException("Process exited with error code " +
+            Integer.valueOf(exitVal).toString());
+      }
+    } catch (InterruptedException e) {
+      LOG.error("Interrupted while getting operating system pid from " +
+          "the shell.", e);
+      return 0L;
+    } catch (IOException e) {
+      LOG.error("Error getting operating system pid from the shell.", e);
+      return 0L;
+    } finally {
+      if (p != null) {
+        p.destroy();
+      }
+    }
+    try {
+      return Long.parseLong(sb.toString());
+    } catch (NumberFormatException e) {
+      LOG.error("Error parsing operating system pid from the shell.", e);
+      return 0L;
+    }
+  }
+
+  /**
+   * Get the process ID by looking at the name of the managed bean for the
+   * runtime system of the Java virtual machine.<p/>
+   *
+   * Although this is undocumented, in the Oracle JVM this name is of the form
+   * [OS_PROCESS_ID]@[HOSTNAME].
+   */
+  private static long getOsPidFromManagementFactory() {
+    try {
+      return Long.parseLong(ManagementFactory.getRuntimeMXBean().
+          getName().split("@")[0]);
+    } catch (NumberFormatException e) {
+      LOG.error("Failed to get the operating system process ID from the name " +
+          "of the managed bean for the JVM.", e);
+      return 0L;
+    }
+  }
+
+  public String get() {
+    return tracerId;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java b/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java
new file mode 100644
index 0000000..26e39f5
--- /dev/null
+++ b/htrace-core4/src/main/java/org/apache/htrace/core/TracerPool.java
@@ -0,0 +1,285 @@
+/*
+ * 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.core;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A pool of Tracer objects.
+ *
+ * There may be more than one {@link Tracer} running inside a single 'process'; for example,
+ * unit tests may spin up a DataNode, a NameNode, and HDFS clients all running in a single JVM
+ * instance, each with its own Tracer. TracerPool is where all Tracer instances register
+ * on creation so Tracers can coordinate around shared resources such as {@link SpanReceiver}
+ * instances. TracerPool takes care of properly cleaning up registered Tracer instances on shutdown.
+ */
+public class TracerPool {
+  private static final Log LOG = LogFactory.getLog(TracerPool.class);
+
+  /**
+   * The global pool of tracer objects.
+   *
+   * This is the pool that new tracers get put into by default.
+   */
+  static final TracerPool GLOBAL = new TracerPool("Global");
+
+  /**
+   * The shutdown hook which closes the Tracers in this pool when the process is
+   * shutting down.
+   */
+  private class SpanReceiverShutdownHook extends Thread {
+    SpanReceiverShutdownHook() {
+      setName("SpanReceiverShutdownHook");
+      setDaemon(false);
+    }
+
+    @Override
+    public void run() {
+      removeAndCloseAllSpanReceivers();
+    }
+  }
+
+  /**
+   * The name of this TracerPool.
+   */
+  private final String name;
+
+  /**
+   * The current span receivers which these tracers are using.
+   *
+   * Can be read locklessly.  Must be written under the lock.
+   * The array itself should never be modified.
+   */
+  private volatile SpanReceiver[] curReceivers;
+
+  /**
+   * The currently installed shutdown hook, or null if no hook has been
+   * installed.
+   */
+  private SpanReceiverShutdownHook shutdownHook;
+
+  /**
+   * The current Tracers.
+   */
+  private final HashSet<Tracer> curTracers;
+
+  /**
+   * Get the global tracer pool.
+   */
+  public static TracerPool getGlobalTracerPool() {
+    return GLOBAL;
+  }
+
+  public TracerPool(String name) {
+    this.name = name;
+    this.shutdownHook = null;
+    this.curTracers = new HashSet<Tracer>();
+    this.curReceivers = new SpanReceiver[0];
+  }
+
+  /**
+   * Return the name of this TracerPool.
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Returns an array of all the current span receivers.
+   *
+   * Note that if the current span receivers change, those changes will not be
+   * reflected in this array.  In other words, this array may be stale.
+   */
+  public SpanReceiver[] getReceivers() {
+    return curReceivers;
+  }
+
+  /**
+   * Add a new span receiver.
+   *
+   * @param receiver        The new receiver to add.
+   *
+   * @return                True if the new receiver was added; false if it
+   *                          already was there.
+   */
+  public synchronized boolean addReceiver(SpanReceiver receiver) {
+    SpanReceiver[] receivers = curReceivers;
+    for (int i = 0; i < receivers.length; i++) {
+      if (receivers[i] == receiver) {
+        LOG.trace(toString() + ": can't add receiver " + receiver.toString() +
+                  " since it is already in this pool.");
+        return false;
+      }
+    }
+    SpanReceiver[] newReceivers =
+        Arrays.copyOf(receivers, receivers.length + 1);
+    newReceivers[receivers.length] = receiver;
+    registerShutdownHookIfNeeded();
+    curReceivers = newReceivers;
+    LOG.trace(toString() + ": added receiver " + receiver.toString());
+    return true;
+  }
+
+  /**
+   * Register the shutdown hook if needed.
+   */
+  private synchronized void registerShutdownHookIfNeeded() {
+    if (shutdownHook != null) {
+      return;
+    }
+    shutdownHook = new SpanReceiverShutdownHook();
+    Runtime.getRuntime().addShutdownHook(shutdownHook);
+    LOG.trace(toString() + ": registered shutdown hook.");
+  }
+
+  /**
+   * Remove a span receiver.
+   *
+   * @param receiver        The receiver to remove.
+   *
+   * @return                True if the receiver was removed; false if it
+   *                          did not exist in this pool.
+   */
+  public synchronized boolean removeReceiver(SpanReceiver receiver) {
+    SpanReceiver[] receivers = curReceivers;
+    for (int i = 0; i < receivers.length; i++) {
+      if (receivers[i] == receiver) {
+        SpanReceiver[] newReceivers = new SpanReceiver[receivers.length - 1];
+        System.arraycopy(receivers, 0, newReceivers, 0, i);
+        System.arraycopy(receivers, i + 1, newReceivers, i,
+            receivers.length - i - 1);
+        curReceivers = newReceivers;
+        LOG.trace(toString() + ": removed receiver " + receiver.toString());
+        return true;
+      }
+    }
+    LOG.trace(toString() + ": can't remove receiver " + receiver.toString() +
+        " since it's not currently in this pool.");
+    return false;
+  }
+
+  /**
+   * Remove and close a span receiver.
+   *
+   * @param receiver        The receiver to remove.
+   *
+   * @return                True if the receiver was removed; false if it
+   *                          did not exist in this pool.
+   */
+  public boolean removeAndCloseReceiver(SpanReceiver receiver) {
+    if (!removeReceiver(receiver)) {
+      return false;
+    }
+    try {
+      LOG.trace(toString() + ": closing receiver " + receiver.toString());
+      receiver.close();
+    } catch (Throwable t) {
+      LOG.error(toString() + ": error closing " + receiver.toString(), t);
+    }
+    return true;
+  }
+
+  /**
+   * Remove and close all of the span receivers.
+   */
+  private synchronized void removeAndCloseAllSpanReceivers() {
+    SpanReceiver[] receivers = curReceivers;
+    curReceivers = new SpanReceiver[0];
+    for (SpanReceiver receiver : receivers) {
+      try {
+        LOG.trace(toString() + ": closing receiver " + receiver.toString());
+        receiver.close();
+      } catch (Throwable t) {
+        LOG.error(toString() + ": error closing " + receiver.toString(), t);
+      }
+    }
+  }
+
+  /**
+   * Given a SpanReceiver class name, return the existing instance of that span
+   * receiver, if possible; otherwise, invoke the callable to create a new
+   * instance.
+   *
+   * @param className       The span receiver class name.
+   * @param conf            The HTrace configuration.
+   * @param classLoader     The class loader to use.
+   *
+   * @return                The SpanReceiver.
+   */
+  public synchronized SpanReceiver loadReceiverType(String className,
+      HTraceConfiguration conf, ClassLoader classLoader) {
+    SpanReceiver[] receivers = curReceivers;
+    for (SpanReceiver receiver : receivers) {
+      if (receiver.getClass().getName().equals(className)) {
+        LOG.trace(toString() + ": returning a reference to receiver " +
+                  receiver.toString());
+        return receiver;
+      }
+    }
+    LOG.trace(toString() + ": creating a new SpanReceiver of type " +
+              className);
+    SpanReceiver receiver = new SpanReceiver.Builder(conf).
+        className(className).
+        classLoader(classLoader).
+        build();
+    addReceiver(receiver);
+    return receiver;
+  }
+
+  /**
+   * Returns an array of all the current Tracers.
+   *
+   * Note that if the current Tracers change, those changes will not be
+   * reflected in this array.  In other words, this array may be stale.
+   */
+  public synchronized Tracer[] getTracers() {
+    return curTracers.toArray(new Tracer[curTracers.size()]);
+  }
+
+  /**
+   * Add a new Tracer.
+   */
+  synchronized void addTracer(Tracer tracer) {
+    if (curTracers.add(tracer)) {
+      LOG.trace(toString() + ": adding tracer " + tracer.toString());
+    }
+  }
+
+  /**
+   * Remove a Tracer.
+   *
+   * If the Tracer removed was the last one, we will close all the SpanReceiver
+   * objects that we're managing.
+   */
+  synchronized void removeTracer(Tracer tracer) {
+    if (curTracers.remove(tracer)) {
+      LOG.trace(toString() + ": removing tracer " + tracer.toString());
+      if (curTracers.size() == 0) {
+        removeAndCloseAllSpanReceivers();
+      }
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "TracerPool(" + name + ")";
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java
new file mode 100644
index 0000000..87ae8e9
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestBadClient.java
@@ -0,0 +1,205 @@
+/*
+ * 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.core;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestBadClient {
+  @After
+  public void clearBadState() {
+    // Clear the bad trace state so that we don't disrupt other unit tests
+    // that run in this JVM.
+    Tracer.threadLocalScope.set(null);
+  }
+
+  /**
+   * Test closing an outer scope when an inner one is still active.
+   */
+  @Test
+  public void TestClosingOuterScope() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestClosingOuterScopeTracer").
+        tracerPool(new TracerPool("TestClosingOuterScope")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
+    boolean gotException = false;
+    TraceScope outerScope = tracer.newScope("outer");
+    TraceScope innerScope = tracer.newScope("inner");
+    try {
+      outerScope.close();
+    } catch (RuntimeException e) {
+      assertThat(e.getMessage(),
+          containsString("it is not the current TraceScope"));
+      gotException = true;
+    }
+    assertTrue("Expected to get exception because of improper " +
+        "scope closure.", gotException);
+    innerScope.close();
+    tracer.close();
+  }
+
+  /**
+   * Test calling detach() two times on a scope object.
+   */
+  @Test
+  public void TestDoubleDetachIsCaught() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestDoubleDetach").
+        tracerPool(new TracerPool("TestDoubleDetachIsCaught")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
+    boolean gotException = false;
+    TraceScope myScope = tracer.newScope("myScope");
+    myScope.detach();
+    try {
+      myScope.detach();
+    } catch (RuntimeException e) {
+      assertThat(e.getMessage(),
+          containsString("it is already detached."));
+      gotException = true;
+    }
+    assertTrue("Expected to get exception because of double TraceScope " +
+        "detach.", gotException);
+    tracer.close();
+  }
+
+  /**
+   * Test calling detach() two times on a scope object.
+   */
+  @Test
+  public void TestDoubleDetachOnNullScope() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestDoubleDetachOnNullScope").
+        tracerPool(new TracerPool("TestDoubleDetachOnNullScope")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "NeverSampler")).build();
+    boolean gotException = false;
+    TraceScope myScope = tracer.newScope("myScope");
+    myScope.detach();
+    try {
+      myScope.detach();
+    } catch (RuntimeException e) {
+      assertThat(e.getMessage(),
+          containsString("it is already detached."));
+      gotException = true;
+    }
+    assertTrue("Expected to get exception because of double TraceScope " +
+        "detach on NullScope.", gotException);
+    tracer.close();
+  }
+
+  /**
+   * Test calling reattach() two times on a scope object.
+   */
+  @Test
+  public void TestDoubleReattachIsCaught() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestDoubleReattach").
+        tracerPool(new TracerPool("TestDoubleReattachIsCaught")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
+    boolean gotException = false;
+    TraceScope myScope = tracer.newScope("myScope");
+    myScope.detach();
+    myScope.reattach();
+    try {
+      myScope.reattach();
+    } catch (RuntimeException e) {
+      assertThat(e.getMessage(),
+          containsString("it is not detached."));
+      gotException = true;
+    }
+    assertTrue("Expected to get exception because of double TraceScope " +
+        "reattach.", gotException);
+    tracer.close();
+  }
+
+  private static class ScopeHolder {
+    TraceScope scope;
+
+    void set(TraceScope scope) {
+      this.scope = scope;
+    }
+  }
+
+  /**
+   * Test correctly passing spans between threads using detach().
+   */
+  @Test
+  public void TestPassingSpanBetweenThreads() throws Exception {
+    final Tracer tracer = new Tracer.Builder().
+        name("TestPassingSpanBetweenThreads").
+        tracerPool(new TracerPool("TestPassingSpanBetweenThreads")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
+    POJOSpanReceiver receiver =
+        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
+    tracer.getTracerPool().addReceiver(receiver);
+    final ScopeHolder scopeHolder = new ScopeHolder();
+    Thread th = new Thread(new Runnable() {
+      @Override
+      public void run() {
+        TraceScope workerScope = tracer.newScope("workerSpan");
+        workerScope.detach();
+        scopeHolder.set(workerScope);
+      }
+    });
+    th.start();
+    th.join();
+    TraceScope workerScope = scopeHolder.scope;
+    SpanId workerScopeId = workerScope.getSpan().getSpanId();
+
+    // Create new scope whose parent is the worker thread's span.
+    workerScope.reattach();
+    TraceScope nested = tracer.newScope("nested");
+    nested.close();
+    // Create another span which also descends from the worker thread's span.
+    TraceScope nested2 = tracer.newScope("nested2");
+    nested2.close();
+
+    // Close the worker thread's span.
+    workerScope.close();
+
+    // We can create another descendant, even though the worker thread's span
+    // has been stopped.
+    TraceScope lateChildScope = tracer.newScope("lateChild", workerScopeId);
+    lateChildScope.close();
+    tracer.close();
+
+    TraceGraph traceGraph = new TraceGraph(receiver.getSpans());
+    Collection<Span> rootSpans =
+        traceGraph.getSpansByParent().find(SpanId.INVALID);
+    Assert.assertEquals(1, rootSpans.size());
+    Assert.assertEquals(workerScopeId,
+        rootSpans.iterator().next().getSpanId());
+    Collection<Span> childSpans =
+        traceGraph.getSpansByParent().find(workerScopeId);
+    Assert.assertEquals(3, childSpans.size());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java
new file mode 100644
index 0000000..e26115d
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestCountSampler.java
@@ -0,0 +1,41 @@
+/*
+ * 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.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCountSampler {
+
+  @Test
+  public void testNext() {
+    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++) {
+      if (half.next())
+        halfCount++;
+      if (hundred.next())
+        hundredCount++;
+    }
+    Assert.assertEquals(2, hundredCount);
+    Assert.assertEquals(100, halfCount);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java
new file mode 100644
index 0000000..06ca189
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTrace.java
@@ -0,0 +1,130 @@
+/*
+ * 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.core;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.htrace.core.TraceGraph.SpansByParent;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestHTrace {
+  @Test
+  public void TestTracerCreateAndClose() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestSimpleScope").
+        tracerPool(new TracerPool("TestTracerCreateAndClose")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "sampler.classes", "AlwaysSampler")).
+        build();
+    POJOSpanReceiver receiver =
+        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
+    tracer.getTracerPool().addReceiver(receiver);
+    tracer.close();
+    Assert.assertTrue(receiver.getSpans().isEmpty());
+  }
+
+  @Test
+  public void TestSimpleScope() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestSimpleScope").
+        tracerPool(new TracerPool("TestSimpleScope")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "sampler.classes", "AlwaysSampler")).
+        build();
+    POJOSpanReceiver receiver =
+        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
+    tracer.getTracerPool().addReceiver(receiver);
+    TraceScope scope = tracer.newScope("Foo");
+    scope.close();
+    tracer.close();
+    Assert.assertEquals(1, receiver.getSpans().size());
+    Span span = receiver.getSpans().iterator().next();
+    Assert.assertEquals(0, span.getParents().length);
+  }
+
+  @Test
+  public void TestCreateSpans() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("TestCreateSpans").
+        tracerPool(new TracerPool("TestCreateSpans")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "sampler.classes", "AlwaysSampler")).
+        build();
+    POJOSpanReceiver receiver =
+        new POJOSpanReceiver(HTraceConfiguration.EMPTY);
+    tracer.getTracerPool().addReceiver(receiver);
+    TraceCreator traceCreator = new TraceCreator(tracer);
+    traceCreator.createSampleRpcTrace();
+    traceCreator.createSimpleTrace();
+    traceCreator.createThreadedTrace();
+    tracer.close();
+    TraceGraph traceGraph = new TraceGraph(receiver.getSpans());
+    Collection<Span> roots = traceGraph.getSpansByParent().find(SpanId.INVALID);
+    Assert.assertTrue("Trace tree must have roots", !roots.isEmpty());
+    Assert.assertEquals(3, roots.size());
+
+    Map<String, Span> descriptionToRootSpan = new HashMap<String, Span>();
+    for (Span root : roots) {
+      descriptionToRootSpan.put(root.getDescription(), root);
+    }
+
+    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
+        TraceCreator.RPC_TRACE_ROOT));
+    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
+        TraceCreator.SIMPLE_TRACE_ROOT));
+    Assert.assertTrue(descriptionToRootSpan.keySet().contains(
+        TraceCreator.THREADED_TRACE_ROOT));
+
+    SpansByParent spansByParentId = traceGraph.getSpansByParent();
+
+    Span rpcTraceRoot = descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT);
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceRoot.getSpanId()).size());
+
+    Span rpcTraceChild1 = spansByParentId.find(rpcTraceRoot.getSpanId())
+        .iterator().next();
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild1.getSpanId()).size());
+
+    Span rpcTraceChild2 = spansByParentId.find(rpcTraceChild1.getSpanId())
+        .iterator().next();
+    Assert.assertEquals(1, spansByParentId.find(rpcTraceChild2.getSpanId()).size());
+
+    Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId())
+        .iterator().next();
+    Assert.assertEquals(0, spansByParentId.find(rpcTraceChild3.getSpanId()).size());
+  }
+
+  @Test(timeout=60000)
+  public void testRootSpansHaveNonZeroSpanId() throws Exception {
+    Tracer tracer = new Tracer.Builder().
+        name("testRootSpansHaveNonZeroSpanId").
+        tracerPool(new TracerPool("testRootSpansHaveNonZeroSpanId")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "sampler.classes", "AlwaysSampler")).build();
+    TraceScope scope = tracer.
+        newScope("myRootSpan", new SpanId(100L, 200L));
+    Assert.assertNotNull(scope);
+    Assert.assertEquals("myRootSpan", scope.getSpan().getDescription());
+    Assert.assertTrue(scope.getSpan().getSpanId().isValid());
+    Assert.assertEquals(100L, scope.getSpan().getSpanId().getHigh());
+    Assert.assertNotEquals(0L, scope.getSpan().getSpanId().getLow());
+    scope.close();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
new file mode 100644
index 0000000..7ca897f
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestHTraceConfiguration.java
@@ -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 org.apache.htrace.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class TestHTraceConfiguration {
+  @Test
+  public void testGetBoolean() throws Exception {
+
+    Map<String, String> m = new HashMap<String, String>();
+    m.put("testTrue", " True");
+    m.put("testFalse", "falsE ");
+    HTraceConfiguration configuration = HTraceConfiguration.fromMap(m);
+
+    // Tests for value being there
+    assertTrue(configuration.getBoolean("testTrue", false));
+    assertFalse(configuration.getBoolean("testFalse", true));
+
+    // Test for absent
+    assertTrue(configuration.getBoolean("absent", true));
+    assertFalse(configuration.getBoolean("absent", false));
+  }
+
+  @Test
+  public void testGetInt() throws Exception {
+    Map<String, String> m = new HashMap<String, String>();
+    m.put("a", "100");
+    m.put("b", "0");
+    m.put("c", "-100");
+    m.put("d", "5");
+
+    HTraceConfiguration configuration = HTraceConfiguration.fromMap(m);
+    assertEquals(100, configuration.getInt("a", -999));
+    assertEquals(0, configuration.getInt("b", -999));
+    assertEquals(-100, configuration.getInt("c", -999));
+    assertEquals(5, configuration.getInt("d", -999));
+    assertEquals(-999, configuration.getInt("absent", -999));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
new file mode 100644
index 0000000..9388707
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
@@ -0,0 +1,65 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+
+public class TestLocalFileSpanReceiver {
+  @Test
+  public void testUniqueLocalTraceFileName() {
+    String filename1 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
+    String filename2 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
+    boolean eq = filename1.equals(filename2);
+    if (System.getProperty("os.name").startsWith("Linux")) {
+      // ${java.io.tmpdir}/[pid]
+      assertTrue(eq);
+    } else {
+      // ${java.io.tmpdir}/[random UUID]
+      assertFalse(eq);
+    }
+  }
+
+  @Test
+  public void testWriteToLocalFile() throws IOException {
+    String traceFileName = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
+    Tracer tracer = new Tracer.Builder().
+        name("testWriteToLocalFileTracer").
+        tracerPool(new TracerPool("testWriteToLocalFile")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "sampler.classes", "AlwaysSampler",
+            "span.receiver.classes", LocalFileSpanReceiver.class.getName(),
+            "local.file.span.receiver.path", traceFileName,
+            "tracer.id", "%{tname}")).
+        build();
+    TraceScope scope = tracer.newScope("testWriteToLocalFile");
+    scope.close();
+    tracer.close();
+
+    ObjectMapper mapper = new ObjectMapper();
+    MilliSpan span = mapper.readValue(new File(traceFileName), MilliSpan.class);
+    assertEquals("testWriteToLocalFile", span.getDescription());
+    assertEquals("testWriteToLocalFileTracer", span.getTracerId());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java
new file mode 100644
index 0000000..7ce1fdb
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestMilliSpan.java
@@ -0,0 +1,145 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ThreadLocalRandom;
+
+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.getSpanId(), got.getSpanId());
+    assertEquals(expected.getTracerId(), got.getTracerId());
+    assertTrue(Arrays.equals(expected.getParents(), got.getParents()));
+    Map<String, String> expectedT = expected.getKVAnnotations();
+    Map<String, String> gotT = got.getKVAnnotations();
+    if (expectedT == null) {
+      assertEquals(null, gotT);
+    } else {
+      assertEquals(expectedT.size(), gotT.size());
+      for (String key : expectedT.keySet()) {
+        assertEquals(expectedT.get(key), gotT.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 SpanId[] { new SpanId(7L, 7L) }).
+        tracerId("b2404.halxg.com:8080").
+        spanId(new SpanId(7L, 8L)).
+        build();
+    String json = span.toJson();
+    MilliSpan dspan = MilliSpan.fromJson(json);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithNegativeLongValue() throws Exception {
+    MilliSpan span = new MilliSpan.Builder().
+        description("foospan").
+        begin(-1L).
+        end(-1L).
+        parents(new SpanId[] { new SpanId(-1L, -1L) }).
+        tracerId("b2404.halxg.com:8080").
+        spanId(new SpanId(-1L, -2L)).
+        build();
+    String json = span.toJson();
+    MilliSpan dspan = MilliSpan.fromJson(json);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithRandomLongValue() throws Exception {
+    SpanId parentId = SpanId.fromRandom();
+    MilliSpan span = new MilliSpan.Builder().
+        description("foospan").
+        begin(ThreadLocalRandom.current().nextLong()).
+        end(ThreadLocalRandom.current().nextLong()).
+        parents(new SpanId[] { parentId }).
+        tracerId("b2404.halxg.com:8080").
+        spanId(parentId.newChildId()).
+        build();
+    String json = span.toJson();
+    MilliSpan dspan = MilliSpan.fromJson(json);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithOptionalFields() throws Exception {
+    MilliSpan.Builder builder = new MilliSpan.Builder().
+        description("foospan").
+        begin(300).
+        end(400).
+        parents(new SpanId[] { }).
+        tracerId("b2408.halxg.com:8080").
+        spanId(new SpanId(111111111L, 111111111L));
+    Map<String, String> traceInfo = new HashMap<String, String>();
+    traceInfo.put("abc", "123");
+    traceInfo.put("def", "456");
+    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();
+    MilliSpan dspan = MilliSpan.fromJson(json);
+    compareSpans(span, dspan);
+  }
+
+  @Test
+  public void testJsonSerializationWithFieldsNotSet() throws Exception {
+    MilliSpan span = new MilliSpan.Builder().build();
+    String json = span.toJson();
+    MilliSpan dspan = MilliSpan.fromJson(json);
+    compareSpans(span, dspan);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java
new file mode 100644
index 0000000..c8ed7f1
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestNullScope.java
@@ -0,0 +1,43 @@
+/*
+ * 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.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestNullScope {
+  private void verifyNullScope(TraceScope scope) {
+    Assert.assertTrue(null == scope.getSpan());
+    Assert.assertFalse(scope.detached);
+    scope.detach();
+    Assert.assertTrue(scope.detached);
+    scope.reattach();
+    Assert.assertFalse(scope.detached);
+    scope.close();
+  }
+
+  @Test
+  public void testNullScope() {
+    Tracer tracer = new Tracer.Builder().
+        name("testNullScope").
+        tracerPool(new TracerPool("testNullScope")).
+        conf(HTraceConfiguration.EMPTY).
+        build();
+    verifyNullScope(tracer.newScope("testNullScope"));
+    verifyNullScope(tracer.newNullScope());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java
new file mode 100644
index 0000000..2305d9f
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSampler.java
@@ -0,0 +1,100 @@
+/*
+ * 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.core;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSampler {
+  private Sampler[] getSamplersFromConf(HTraceConfiguration conf) {
+    Tracer tracer = new Tracer.Builder().
+        name("MyTracer").
+        tracerPool(new TracerPool("getSamplersFromConf")).
+        conf(conf).
+        build();
+    Sampler[] samplers = tracer.getSamplers();
+    tracer.close();
+    return samplers;
+  }
+
+  private void checkArrayContains(List<Class<? extends Sampler>> expected,
+                                  Sampler[] samplers) {
+    for (Iterator<Class<? extends Sampler>> iter = expected.iterator();
+         iter.hasNext(); ) {
+      Class<? extends Sampler> samplerClass = iter.next();
+      boolean found = false;
+      for (int i = 0; i < samplers.length; i++) {
+        if (samplers[i] != null) {
+          if (samplers[i].getClass().equals(samplerClass)) {
+            samplers[i] = null;
+            found = true;
+            break;
+          }
+        }
+      }
+      Assert.assertTrue("Failed to find sampler class " +
+          samplerClass.getName(), found);
+    }
+    for (int i = 0; i < samplers.length; i++) {
+      if (samplers[i] != null) {
+        Assert.fail("Got extra sampler of type " +
+            samplers.getClass().getName());
+      }
+    }
+  }
+
+  private void checkArrayContains(Class<? extends Sampler> expected, Sampler[] samplers) {
+    LinkedList<Class<? extends Sampler>> expectedList =
+        new LinkedList<Class<? extends Sampler>>();
+    expectedList.add(expected);
+    checkArrayContains(expectedList, samplers);
+  }
+
+  @Test
+  public void testTracerBuilderCreatesCorrectSamplers() {
+    Sampler[] samplers = getSamplersFromConf(HTraceConfiguration.
+        fromKeyValuePairs("sampler.classes", "AlwaysSampler"));
+    checkArrayContains(AlwaysSampler.class, samplers);
+
+    samplers = getSamplersFromConf(HTraceConfiguration.
+        fromKeyValuePairs("sampler.classes", "NeverSampler"));
+    checkArrayContains(NeverSampler.class, samplers);
+
+    samplers = getSamplersFromConf(HTraceConfiguration.
+        fromKeyValuePairs("sampler.classes", "NonExistentSampler"));
+    Assert.assertEquals(0, samplers.length);
+
+    samplers = getSamplersFromConf(HTraceConfiguration.EMPTY);
+    Assert.assertEquals(0, samplers.length);
+  }
+
+  @Test
+  public void testAlwaysSampler() {
+    AlwaysSampler sampler = new AlwaysSampler(HTraceConfiguration.EMPTY);
+    Assert.assertTrue(sampler.next());
+  }
+
+  @Test
+  public void testNeverSampler() {
+    NeverSampler sampler = new NeverSampler(HTraceConfiguration.EMPTY);
+    Assert.assertTrue(!sampler.next());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java
new file mode 100644
index 0000000..bb57368
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanId.java
@@ -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 org.apache.htrace.core;
+
+import java.util.Random;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestSpanId {
+  private void testRoundTrip(SpanId id) throws Exception {
+    String str = id.toString();
+    SpanId id2 = SpanId.fromString(str);
+    Assert.assertEquals(id, id2);
+  }
+
+  @Test
+  public void testToStringAndFromString() throws Exception {
+    testRoundTrip(SpanId.INVALID);
+    testRoundTrip(new SpanId(0x1234567812345678L, 0x1234567812345678L));
+    testRoundTrip(new SpanId(0xf234567812345678L, 0xf234567812345678L));
+    testRoundTrip(new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL));
+    Random rand = new Random(12345);
+    for (int i = 0; i < 100; i++) {
+      testRoundTrip(new SpanId(rand.nextLong(), rand.nextLong()));
+    }
+  }
+
+  @Test
+  public void testValidAndInvalidIds() throws Exception {
+    Assert.assertFalse(SpanId.INVALID.isValid());
+    Assert.assertTrue(
+        new SpanId(0x1234567812345678L, 0x1234567812345678L).isValid());
+    Assert.assertTrue(
+        new SpanId(0xf234567812345678L, 0xf234567812345678L).isValid());
+  }
+
+  private void expectLessThan(SpanId a, SpanId b) throws Exception {
+    int cmp = a.compareTo(b);
+    Assert.assertTrue("Expected " + a + " to be less than " + b,
+        (cmp < 0));
+    int cmp2 = b.compareTo(a);
+    Assert.assertTrue("Expected " + b + " to be greater than " + a,
+        (cmp2 > 0));
+  }
+
+  @Test
+  public void testIdComparisons() throws Exception {
+    expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L),
+                   new SpanId(0x0000000000000001L, 0x0000000000000002L));
+    expectLessThan(new SpanId(0x0000000000000001L, 0x0000000000000001L),
+                   new SpanId(0x0000000000000002L, 0x0000000000000000L));
+    expectLessThan(SpanId.INVALID,
+                   new SpanId(0xffffffffffffffffL, 0xffffffffffffffffL));
+    expectLessThan(new SpanId(0x1234567812345678L, 0x1234567812345678L),
+                   new SpanId(0x1234567812345678L, 0xf234567812345678L));
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/3c20489f/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
----------------------------------------------------------------------
diff --git a/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
new file mode 100644
index 0000000..b97d624
--- /dev/null
+++ b/htrace-core4/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
@@ -0,0 +1,127 @@
+/*
+ * 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.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class TestSpanReceiverBuilder {
+  private static final Log LOG =
+      LogFactory.getLog(TestSpanReceiverBuilder.class);
+
+  private List<SpanReceiver> createSpanReceivers(String classes) {
+    Tracer tracer = new Tracer.Builder().
+        name("MyTracer").
+        tracerPool(new TracerPool("createSpanReceivers")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            "span.receiver.classes", classes)).
+        build();
+    SpanReceiver[] receivers = tracer.getTracerPool().getReceivers();
+    tracer.close();
+    LinkedList<SpanReceiver> receiverList = new LinkedList<SpanReceiver>();
+    for (SpanReceiver item: receivers) {
+      receiverList.add(item);
+    }
+    return receiverList;
+  }
+
+  @Test
+  public void TestCreateStandardSpanReceivers() {
+    List<SpanReceiver> receivers;
+    receivers = createSpanReceivers("");
+    Assert.assertTrue(receivers.isEmpty());
+    receivers = createSpanReceivers("POJOSpanReceiver");
+    Assert.assertTrue(receivers.get(0).getClass().getName().
+        equals("org.apache.htrace.core.POJOSpanReceiver"));
+    receivers = createSpanReceivers(
+               "org.apache.htrace.core.StandardOutSpanReceiver");
+    Assert.assertTrue(receivers.get(0).getClass().getName().
+        equals("org.apache.htrace.core.StandardOutSpanReceiver"));
+    receivers = createSpanReceivers(
+               "POJOSpanReceiver;StandardOutSpanReceiver");
+    Assert.assertEquals(2, receivers.size());
+    for (Iterator<SpanReceiver> iter = receivers.iterator(); iter.hasNext();) {
+      SpanReceiver receiver = iter.next();
+      if (receiver.getClass().getName().equals(
+          "org.apache.htrace.core.POJOSpanReceiver")) {
+        iter.remove();
+        break;
+      }
+    }
+    for (Iterator<SpanReceiver> iter = receivers.iterator(); iter.hasNext();) {
+      SpanReceiver receiver = iter.next();
+      if (receiver.getClass().getName().equals(
+          "org.apache.htrace.core.StandardOutSpanReceiver")) {
+        iter.remove();
+        break;
+      }
+    }
+    Assert.assertEquals(0, receivers.size());
+  }
+
+  public static class GoodSpanReceiver extends SpanReceiver {
+    public GoodSpanReceiver(HTraceConfiguration conf) {
+    }
+
+    @Override
+    public void receiveSpan(Span span) {
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+  }
+
+  public static class BadSpanReceiver extends SpanReceiver {
+    public BadSpanReceiver(HTraceConfiguration conf) {
+      throw new RuntimeException("Can't create BadSpanReceiver");
+    }
+
+    @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 {
+    List<SpanReceiver> receivers;
+    receivers = createSpanReceivers(
+        GoodSpanReceiver.class.getName());
+    Assert.assertEquals(1, receivers.size());
+    Assert.assertTrue(receivers.get(0).getClass().getName().
+        contains("GoodSpanReceiver"));
+    receivers = createSpanReceivers(
+        BadSpanReceiver.class.getName());
+    Assert.assertEquals(0, receivers.size());
+  }
+}