Repository: incubator-htrace
Updated Branches:
  refs/heads/master 0f873fdec -> 7997d2089


http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/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
new file mode 100644
index 0000000..ea4d145
--- /dev/null
+++ b/htrace-core/src/main/java/org/apache/htrace/core/TracerPool.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.htrace.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * A pool of Tracer objects.
+ */
+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;
+    int j = 0;
+    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;
+    int j = 0;
+    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 + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/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
index 54de21b..e30ea3e 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TestBadClient.java
@@ -16,6 +16,12 @@
  */
 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;
@@ -24,57 +30,121 @@ import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.io.File;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-
 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 TracerBuilder().
+        name("TestClosingOuterScopeTracer").
+        tracerPool(new TracerPool("TestClosingOuterScope")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
     boolean gotException = false;
-    TraceScope outerScope = Trace.startSpan("outer", AlwaysSampler.INSTANCE);
-    TraceScope innerScope = Trace.startSpan("inner");
+    TraceScope outerScope = tracer.newScope("outer");
+    TraceScope innerScope = tracer.newScope("inner");
     try {
       outerScope.close();
     } catch (RuntimeException e) {
       assertThat(e.getMessage(),
-          containsString("You have probably forgotten to close or detach"));
+          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 TestDoubleDetach() throws Exception {
+  public void TestDoubleDetachIsCaught() throws Exception {
+    Tracer tracer = new TracerBuilder().
+        name("TestDoubleDetach").
+        tracerPool(new TracerPool("TestDoubleDetachIsCaught")).
+        conf(HTraceConfiguration.
+            fromKeyValuePairs("sampler.classes", "AlwaysSampler")).build();
     boolean gotException = false;
-    TraceScope myScope = Trace.startSpan("myScope", AlwaysSampler.INSTANCE);
+    TraceScope myScope = tracer.newScope("myScope");
     myScope.detach();
     try {
       myScope.detach();
     } catch (RuntimeException e) {
       assertThat(e.getMessage(),
-          containsString("it has already been detached."));
+          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 TracerBuilder().
+        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 TracerBuilder().
+        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 SpanHolder {
-    Span span;
+  private static class ScopeHolder {
+    TraceScope scope;
 
-    void set(Span span) {
-      this.span = span;
+    void set(TraceScope scope) {
+      this.scope = scope;
     }
   }
 
@@ -83,64 +153,53 @@ public class TestBadClient {
    */
   @Test
   public void TestPassingSpanBetweenThreads() throws Exception {
-    final SpanHolder spanHolder = new SpanHolder();
+    final Tracer tracer = new TracerBuilder().
+        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 = Trace.startSpan("workerSpan",
-            AlwaysSampler.INSTANCE);
-        spanHolder.set(workerScope.getSpan());
+        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. 
-    TraceScope outermost = Trace.startSpan("outermost", spanHolder.span);
-    TraceScope nested = Trace.startSpan("nested");
+    // Create new scope whose parent is the worker thread's span.
+    workerScope.reattach();
+    TraceScope nested = tracer.newScope("nested");
     nested.close();
-    outermost.close();
     // Create another span which also descends from the worker thread's span.
-    TraceScope nested2 = Trace.startSpan("nested2", spanHolder.span);
+    TraceScope nested2 = tracer.newScope("nested2");
     nested2.close();
 
     // Close the worker thread's span.
-    spanHolder.span.stop();
+    workerScope.close();
 
     // We can create another descendant, even though the worker thread's span
     // has been stopped.
-    TraceScope lateChildScope = Trace.startSpan("lateChild", spanHolder.span);
+    TraceScope lateChildScope = tracer.newScope("lateChild", workerScopeId);
     lateChildScope.close();
-  }
-
-  /**
-   * Test trying to manually set our TraceScope's parent in a case where there
-   * is a currently active span.
-   */
-  @Test
-  public void TestIncorrectStartSpan() throws Exception {
-    // Create new scope
-    TraceScope outermost = Trace.startSpan("outermost",
-        AlwaysSampler.INSTANCE);
-    // Create nested scope
-    TraceScope nested = Trace.startSpan("nested", outermost.getSpan()); 
-    // Error
-    boolean gotException = false;
-    try {
-      TraceScope error = Trace.startSpan("error", outermost.getSpan()); 
-      error.close();
-    } catch (RuntimeException e) {
-      assertThat(e.getMessage(),
-          containsString("there is already a currentSpan"));
-      gotException = true;
-    }
-    assertTrue("Expected to get exception because of incorrect startSpan.",
-        gotException);
-  }
+    tracer.close();
 
-  @After
-  public void resetCurrentSpan() {
-    Tracer.getInstance().setCurrentSpan(null);
+    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/7997d208/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
index f1839cb..1771407 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TestHTrace.java
@@ -16,7 +16,6 @@
  */
 package org.apache.htrace.core;
 
-import java.io.File;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -24,93 +23,108 @@ import java.util.Map;
 import org.apache.htrace.core.TraceGraph.SpansByParent;
 
 import org.junit.Assert;
-import org.junit.Rule;
 import org.junit.Test;
 
 public class TestHTrace {
+  @Test
+  public void TestTracerCreateAndClose() throws Exception {
+    Tracer tracer = new TracerBuilder().
+        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());
+  }
 
-  @Rule
-  public TraceCreator traceCreator = new TraceCreator();
-
-  public static final String SPAN_FILE_FLAG = "spanFile";
-
-  /**
-   * Basic system test of HTrace.
-   *
-   * @throws Exception
-   */
   @Test
-  public void testHtrace() throws Exception {
-    final int numTraces = 3;
-    String fileName = System.getProperty(SPAN_FILE_FLAG);
+  public void TestSimpleScope() throws Exception {
+    Tracer tracer = new TracerBuilder().
+        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);
+  }
 
-    // writes spans to a file if one is provided to maven with
-    // -DspanFile="FILENAME", otherwise writes to standard out.
-    if (fileName != null) {
-      File f = new File(fileName);
-      File parent = f.getParentFile();
-      if (parent != null && !parent.exists() && !parent.mkdirs()) {
-        throw new IllegalArgumentException("Couldn't create file: "
-            + fileName);
-      }
-      HashMap<String, String> conf = new HashMap<String, String>();
-      conf.put("local-file-span-receiver.path", fileName);
-      LocalFileSpanReceiver receiver =
-          new LocalFileSpanReceiver(HTraceConfiguration.fromMap(conf));
-      traceCreator.addReceiver(receiver);
-    } else {
-      traceCreator.addReceiver(new 
StandardOutSpanReceiver(HTraceConfiguration.EMPTY));
+  @Test
+  public void TestCreateSpans() throws Exception {
+    Tracer tracer = new TracerBuilder().
+        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);
     }
 
-    traceCreator.addReceiver(new POJOSpanReceiver(HTraceConfiguration.EMPTY){
-      @Override
-      public void close() {
-        TraceGraph traceGraph = new TraceGraph(getSpans());
-        Collection<Span> roots = 
traceGraph.getSpansByParent().find(SpanId.INVALID);
-        Assert.assertTrue("Trace tree must have roots", !roots.isEmpty());
-        Assert.assertEquals(numTraces, 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));
 
-        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();
 
-        SpansByParent spansByParentId = traceGraph.getSpansByParent();
-        Span rpcTraceRoot = 
descriptionToRootSpan.get(TraceCreator.RPC_TRACE_ROOT);
-        Assert.assertEquals(1, 
spansByParentId.find(rpcTraceRoot.getSpanId()).size());
+    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 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 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());
-      }
-    });
-
-    traceCreator.createThreadedTrace();
-    traceCreator.createSimpleTrace();
-    traceCreator.createSampleRpcTrace();
+    Span rpcTraceChild3 = spansByParentId.find(rpcTraceChild2.getSpanId())
+        .iterator().next();
+    Assert.assertEquals(0, 
spansByParentId.find(rpcTraceChild3.getSpanId()).size());
   }
 
   @Test(timeout=60000)
   public void testRootSpansHaveNonZeroSpanId() throws Exception {
-    TraceScope scope = Trace.startSpan("myRootSpan", new SpanId(100L, 200L));
+    Tracer tracer = new TracerBuilder().
+        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.assertEquals(100L, scope.getSpan().getSpanId().getHigh());
     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/7997d208/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
index 90a009a..b4a579b 100644
--- 
a/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
+++ 
b/htrace-core/src/test/java/org/apache/htrace/core/TestLocalFileSpanReceiver.java
@@ -16,25 +16,21 @@
  */
 package org.apache.htrace.core;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
 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 java.util.HashMap;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class TestLocalFileSpanReceiver {
   @Test
   public void testUniqueLocalTraceFileName() {
     String filename1 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    System.out.println("##### :" + filename1);
     String filename2 = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    System.out.println("##### :" + filename2);
     boolean eq = filename1.equals(filename2);
     if (System.getProperty("os.name").startsWith("Linux")) {
       // ${java.io.tmpdir}/[pid]
@@ -48,23 +44,22 @@ public class TestLocalFileSpanReceiver {
   @Test
   public void testWriteToLocalFile() throws IOException {
     String traceFileName = LocalFileSpanReceiver.getUniqueLocalTraceFileName();
-    HashMap<String, String> confMap = new HashMap<String, String>();
-    confMap.put(LocalFileSpanReceiver.PATH_KEY, traceFileName);
-    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
-                LocalFileSpanReceiver.class.getName());
-    confMap.put(TracerId.TRACER_ID_KEY, "testTrid");
-    SpanReceiver rcvr =
-        new SpanReceiverBuilder(HTraceConfiguration.fromMap(confMap))
-            .logErrors(false).build();
-    Trace.addReceiver(rcvr);
-    TraceScope ts = Trace.startSpan("testWriteToLocalFile", Sampler.ALWAYS);
-    ts.close();
-    Trace.removeReceiver(rcvr);
-    rcvr.close();
+    Tracer tracer = new TracerBuilder().
+        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("testTrid", span.getTracerId());
+    assertEquals("testWriteToLocalFileTracer", span.getTracerId());
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/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
index 3fa9210..592ce79 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TestNullScope.java
@@ -20,15 +20,24 @@ 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() {
-    Assert.assertTrue(!Trace.isTracing());
-    TraceScope tc = Trace.startSpan("NullScopeSingleton");
-    Assert.assertTrue(tc == NullScope.INSTANCE);
-    tc.detach();
-    tc.detach(); // should not fail even if called multiple times.
-    Assert.assertFalse(tc.isDetached());
-    tc.close();
-    tc.close(); // should not fail even if called multiple times.
+    Tracer tracer = new TracerBuilder().
+        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/7997d208/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
index e11799b..aa57d2c 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TestSampler.java
@@ -16,40 +16,85 @@
  */
 package org.apache.htrace.core;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 
 import org.junit.Assert;
 import org.junit.Test;
 
 public class TestSampler {
-  @Test
-  public void testSamplerBuilder() {
-    Sampler alwaysSampler = new SamplerBuilder(
-        HTraceConfiguration.fromKeyValuePairs("sampler", "AlwaysSampler")).
+  private Sampler[] getSamplersFromConf(HTraceConfiguration conf) {
+    Tracer tracer = new TracerBuilder().
+        name("MyTracer").
+        tracerPool(new TracerPool("getSamplersFromConf")).
+        conf(conf).
         build();
-    Assert.assertEquals(AlwaysSampler.class, alwaysSampler.getClass());
+    Sampler[] samplers = tracer.getSamplers();
+    tracer.close();
+    return samplers;
+  }
 
-    Sampler neverSampler = new SamplerBuilder(
-        HTraceConfiguration.fromKeyValuePairs("sampler", "NeverSampler")).
-        build();
-    Assert.assertEquals(NeverSampler.class, neverSampler.getClass());
+  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());
+      }
+    }
+  }
 
-    Sampler neverSampler2 = new SamplerBuilder(HTraceConfiguration.
-        fromKeyValuePairs("sampler", "NonExistentSampler")).
-        build();
-    Assert.assertEquals(NeverSampler.class, neverSampler2.getClass());
+  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);
+  }
 
-    Sampler neverSampler3 = new SamplerBuilder(HTraceConfiguration.
-        fromKeyValuePairs("sampler.is.not.defined", "NonExistentSampler")).
-        build();
-    Assert.assertEquals(NeverSampler.class, neverSampler3.getClass());
+  @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() {
-    TraceScope cur = Trace.startSpan("test");
-    Assert.assertNotNull(cur);
-    cur.close();
+    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/7997d208/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
index 79795e4..e0ce2ac 100644
--- 
a/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
+++ 
b/htrace-core/src/test/java/org/apache/htrace/core/TestSpanReceiverBuilder.java
@@ -16,93 +16,87 @@
  */
 package org.apache.htrace.core;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
 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);
 
-  /**
-   * Test that if no span receiver is configured, the builder returns null.
-   */
-  @Test
-  public void testGetNullSpanReceiver() {
-    SpanReceiverBuilder builder =
-        new SpanReceiverBuilder(HTraceConfiguration.EMPTY).logErrors(false);
-    SpanReceiver rcvr = builder.build();
-    Assert.assertEquals(null, rcvr);
-  }
-
-  private static SpanReceiver createSpanReceiver(Map<String, String> m) {
-    HTraceConfiguration hconf = HTraceConfiguration.fromMap(m);
-    SpanReceiverBuilder builder =
-        new SpanReceiverBuilder(hconf).
-            logErrors(false);
-    return builder.build();
+  private List<SpanReceiver> createSpanReceivers(String classes) {
+    Tracer tracer = new TracerBuilder().
+        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;
   }
 
-  private static final File TMPDIR =
-      new File(System.getProperty("java.io.tmpdir"));
-
-  /**
-   * Test getting various SpanReceiver objects.
-   */
   @Test
-  public void testGetSpanReceivers() throws Exception {
-    HashMap<String, String> confMap = new HashMap<String, String>();
-
-    // Create LocalFileSpanReceiver
-    File testFile = new File(TMPDIR, UUID.randomUUID().toString());
-    try {
-      confMap.put(LocalFileSpanReceiver.PATH_KEY, testFile.getAbsolutePath());
-      confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
-          "org.apache.htrace.core.LocalFileSpanReceiver");
-      SpanReceiver rcvr = createSpanReceiver(confMap);
-      Assert.assertNotNull(rcvr);
-      Assert.assertEquals("org.apache.htrace.core.LocalFileSpanReceiver",
-          rcvr.getClass().getName());
-      rcvr.close();
-    } finally {
-      if (!testFile.delete()) {
-        LOG.debug("failed to delete " + testFile); // keep findbugs happy
+  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());
+  }
 
-    // Create POJOSpanReceiver
-    confMap.remove(LocalFileSpanReceiver.PATH_KEY);
-    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY, 
"POJOSpanReceiver");
-    SpanReceiver rcvr = createSpanReceiver(confMap);
-    Assert.assertEquals("org.apache.htrace.core.POJOSpanReceiver",
-        rcvr.getClass().getName());
-    rcvr.close();
+  public static class GoodSpanReceiver extends SpanReceiver {
+    public GoodSpanReceiver(HTraceConfiguration conf) {
+    }
 
-    // Create StandardOutSpanReceiver
-    confMap.remove(LocalFileSpanReceiver.PATH_KEY);
-    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
-        "org.apache.htrace.core.StandardOutSpanReceiver");
-    rcvr = createSpanReceiver(confMap);
-    Assert.assertEquals("org.apache.htrace.core.StandardOutSpanReceiver",
-        rcvr.getClass().getName());
-    rcvr.close();
-  }
+    @Override
+    public void receiveSpan(Span span) {
+    }
 
-  public static class TestSpanReceiver implements SpanReceiver {
-    final static String SUCCEEDS = "test.span.receiver.succeeds";
+    @Override
+    public void close() throws IOException {
+    }
+  }
 
-    public TestSpanReceiver(HTraceConfiguration conf) {
-      if (conf.get(SUCCEEDS) == null) {
-        throw new RuntimeException("Can't create TestSpanReceiver: " +
-            "invalid configuration.");
-      }
+  public static class BadSpanReceiver extends SpanReceiver {
+    public BadSpanReceiver(HTraceConfiguration conf) {
+      throw new RuntimeException("Can't create BadSpanReceiver");
     }
 
     @Override
@@ -120,20 +114,14 @@ public class TestSpanReceiverBuilder {
    */
   @Test
   public void testGetSpanReceiverWithConstructorError() throws Exception {
-    HashMap<String, String> confMap = new HashMap<String, String>();
-
-    // Create TestSpanReceiver
-    confMap.put(SpanReceiverBuilder.SPAN_RECEIVER_CONF_KEY,
-        TestSpanReceiver.class.getName());
-    confMap.put(TestSpanReceiver.SUCCEEDS, "true");
-    SpanReceiver rcvr = createSpanReceiver(confMap);
-    Assert.assertEquals(TestSpanReceiver.class.getName(),
-        rcvr.getClass().getName());
-    rcvr.close();
-
-    // Fail to create TestSpanReceiver
-    confMap.remove(TestSpanReceiver.SUCCEEDS);
-    rcvr = createSpanReceiver(confMap);
-    Assert.assertEquals(null, rcvr);
+    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/7997d208/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
index ac43653..1e842c5 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TestTracerId.java
@@ -16,13 +16,17 @@
  */
 package org.apache.htrace.core;
 
+import static org.junit.Assert.assertEquals;
+
 import java.io.IOException;
+
 import org.junit.Test;
-import static org.junit.Assert.assertEquals;
 
 public class TestTracerId {
   private void testTracerIdImpl(String expected, String fmt) {
-    assertEquals(expected, new TracerId(fmt).get());
+    assertEquals(expected, new TracerId(
+        HTraceConfiguration.fromKeyValuePairs(TracerId.TRACER_ID_KEY, fmt),
+        "TracerName").get());
   }
 
   @Test
@@ -36,12 +40,13 @@ public class TestTracerId {
 
   @Test
   public void testSubstitutionVariables() throws IOException {
-    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("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}");
+        "Foo.%{pid}");
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/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
index f6ae97f..b843999 100644
--- a/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java
+++ b/htrace-core/src/test/java/org/apache/htrace/core/TraceCreator.java
@@ -16,54 +16,25 @@
  */
 package org.apache.htrace.core;
 
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.util.Collection;
 import java.util.Random;
 import java.util.concurrent.ThreadLocalRandom;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
 
 /**
  * Does some stuff and traces it.
  */
-public class TraceCreator implements TestRule {
-  private final List<SpanReceiver> receivers = new ArrayList<SpanReceiver>();
-
+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";
 
-  public TraceCreator addReceiver(SpanReceiver receiver) {
-    Trace.addReceiver(receiver);
-    this.receivers.add(receiver);
-    return this;
-  }
+  private final Tracer tracer;
 
-  @Override
-  public Statement apply(final Statement base, Description description) {
-    return new Statement() {
-      @Override
-      public void evaluate() throws Throwable {
-        try {
-          base.evaluate();
-          for (SpanReceiver receiver : receivers) {
-            receiver.close();
-          }
-        } finally {
-          for (SpanReceiver receiver : receivers) {
-            Trace.removeReceiver(receiver);
-          }
-        }
-      }
-    };
+  public TraceCreator(Tracer tracer) {
+    this.tracer = tracer;
   }
 
   public void createSampleRpcTrace() {
-    TraceScope s = Trace.startSpan(RPC_TRACE_ROOT, Sampler.ALWAYS);
+    TraceScope s = tracer.newScope(RPC_TRACE_ROOT);
     try {
       pretendRpcSend();
     } finally {
@@ -72,7 +43,7 @@ public class TraceCreator implements TestRule {
   }
 
   public void createSimpleTrace() {
-    TraceScope s = Trace.startSpan(SIMPLE_TRACE_ROOT, Sampler.ALWAYS);
+    TraceScope s = tracer.newScope(SIMPLE_TRACE_ROOT);
     try {
       importantWork1();
     } finally {
@@ -84,14 +55,14 @@ public class TraceCreator implements TestRule {
    * Creates the demo trace (will create different traces from call to call).
    */
   public void createThreadedTrace() {
-    TraceScope s = Trace.startSpan(THREADED_TRACE_ROOT, Sampler.ALWAYS);
+    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(Trace.wrap(new MyRunnable()));
+        threads[i] = new Thread(tracer.wrap(new MyRunnable(), null));
       }
       for (int i = 0; i < numThreads; i++) {
         threads[i].start();
@@ -109,7 +80,7 @@ public class TraceCreator implements TestRule {
   }
 
   private void importantWork1() {
-    TraceScope cur = Trace.startSpan("important work 1");
+    TraceScope cur = tracer.newScope("important work 1");
     try {
       Thread.sleep((long) (2000 * Math.random()));
       importantWork2();
@@ -121,7 +92,7 @@ public class TraceCreator implements TestRule {
   }
 
   private void importantWork2() {
-    TraceScope cur = Trace.startSpan("important work 2");
+    TraceScope cur = tracer.newScope("important work 2");
     try {
       Thread.sleep((long) (2000 * Math.random()));
     } catch (InterruptedException e) {
@@ -142,7 +113,7 @@ public class TraceCreator implements TestRule {
       } catch (InterruptedException ie) {
         Thread.currentThread().interrupt();
       } catch (ArithmeticException ae) {
-        TraceScope c = Trace.startSpan("dealing with arithmetic exception.");
+        TraceScope c = tracer.newScope("dealing with arithmetic exception.");
         try {
           Thread.sleep((long) (3000 * Math.random()));
         } catch (InterruptedException ie1) {
@@ -155,11 +126,12 @@ public class TraceCreator implements TestRule {
   }
 
   public void pretendRpcSend() {
-    pretendRpcReceiveWithTraceInfo(Trace.currentSpan());
+    Span span = tracer.getCurrentSpan();
+    pretendRpcReceiveWithTraceInfo(span.getSpanId());
   }
 
-  public void pretendRpcReceiveWithTraceInfo(Span parent) {
-    TraceScope s = Trace.startSpan("received RPC", parent);
+  public void pretendRpcReceiveWithTraceInfo(SpanId parentId) {
+    TraceScope s = tracer.newScope("received RPC", parentId);
     try {
       importantWork1();
     } finally {

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java 
b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
index 4820bd0..199f93d 100644
--- a/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
+++ b/htrace-flume/src/main/java/org/apache/htrace/impl/FlumeSpanReceiver.java
@@ -44,7 +44,7 @@ import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanReceiver;
 import org.apache.htrace.core.TracerId;
 
-public class FlumeSpanReceiver implements SpanReceiver {
+public class FlumeSpanReceiver extends SpanReceiver {
   private static final Log LOG = LogFactory.getLog(FlumeSpanReceiver.class);
 
   public static final String NUM_THREADS_KEY = "htrace.flume.num-threads";
@@ -90,12 +90,10 @@ public class FlumeSpanReceiver implements SpanReceiver {
   private int maxSpanBatchSize;
   private String flumeHostName;
   private int flumePort;
-  private final TracerId tracerId;
 
   public FlumeSpanReceiver(HTraceConfiguration conf) {
     this.queue = new ArrayBlockingQueue<Span>(1000);
     this.tf = new SimpleThreadFactory();
-    this.tracerId = new TracerId(conf);
     configure(conf);
   }
 
@@ -274,9 +272,6 @@ public class FlumeSpanReceiver implements SpanReceiver {
   public void receiveSpan(Span span) {
     if (running.get()) {
       try {
-        if (span.getTracerId().isEmpty()) {
-          span.setTracerId(tracerId.get());
-        }
         this.queue.add(span);
       } catch (IllegalStateException e) {
         LOG.error("Error trying to append span (" +

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java 
b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
index d5037c8..6cdab2e 100644
--- 
a/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
+++ 
b/htrace-flume/src/test/java/org/apache/htrace/impl/TestFlumeSpanReceiver.java
@@ -17,12 +17,17 @@
 
 package org.apache.htrace.impl;
 
-import org.apache.htrace.core.HTraceConfiguration;
+import org.apache.htrace.core.AlwaysSampler;
 import org.apache.htrace.core.HTraceConfiguration;
 import org.apache.htrace.core.MilliSpan;
 import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanId;
 import org.apache.htrace.core.TraceCreator;
+import org.apache.htrace.core.TraceScope;
+import org.apache.htrace.core.Tracer;
+import org.apache.htrace.core.TracerBuilder;
+import org.apache.htrace.core.TracerPool;
+
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -31,56 +36,55 @@ import java.io.IOException;
 
 public class TestFlumeSpanReceiver {
   @Rule
-  public TraceCreator traceCreator = new TraceCreator();
-  @Rule
   public FakeFlume flumeServer = new FakeFlume();
 
-  @Test
-  public void testSimpleTraces() throws IOException, InterruptedException {
-    traceCreator.addReceiver(new FlumeSpanReceiver(
-        HTraceConfiguration.fromKeyValuePairs(
-            FlumeSpanReceiver.FLUME_PORT_KEY, 
Integer.toString(flumeServer.getPort())
-        )
-    ));
+  private Tracer newTracer() {
+    return new TracerBuilder().
+        name("FlumeTracer").
+        tracerPool(new TracerPool("newTracer")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+            FlumeSpanReceiver.FLUME_PORT_KEY,
+            Integer.toString(flumeServer.getPort()),
+            "span.receiver.classes", FlumeSpanReceiver.class.getName(),
+            "sampler.classes", AlwaysSampler.class.getName()
+        )).build();
+  }
 
+  @Test(timeout=120000)
+  public void testSimpleTraces() throws IOException, InterruptedException {
+    Tracer tracer = newTracer();
     Span rootSpan = new MilliSpan.Builder().
         description("root").
         spanId(new SpanId(100, 100)).
         tracerId("test").
         begin(System.currentTimeMillis()).
         build();
-    Span innerOne = rootSpan.child("Some good work");
-    Span innerTwo = innerOne.child("Some more good work");
-    innerTwo.stop();
-    
Assert.assertTrue(flumeServer.nextEventBodyAsString().contains(innerTwo.getDescription()));
-    innerOne.stop();
-    
Assert.assertTrue(flumeServer.nextEventBodyAsString().contains(innerOne.getDescription()));
+    TraceScope rootScope = tracer.newScope("root");
+    TraceScope innerOne = tracer.newScope("innerOne");
+    TraceScope innerTwo = tracer.newScope("innerTwo");
+    innerTwo.close();
+    
Assert.assertTrue(flumeServer.nextEventBodyAsString().contains("innerTwo"));
+    innerOne.close();
+    
Assert.assertTrue(flumeServer.nextEventBodyAsString().contains("innerOne"));
     rootSpan.addKVAnnotation("foo", "bar");
     rootSpan.addTimelineAnnotation("timeline");
-    rootSpan.stop();
-    
Assert.assertTrue(flumeServer.nextEventBodyAsString().contains(rootSpan.getDescription()));
+    rootScope.close();
+    Assert.assertTrue(flumeServer.nextEventBodyAsString().contains("root"));
+    tracer.close();
   }
 
-  @Test
+  @Test(timeout=120000)
   public void testConcurrency() throws IOException {
-    traceCreator.addReceiver(new FlumeSpanReceiver(
-        HTraceConfiguration.fromKeyValuePairs(
-            FlumeSpanReceiver.FLUME_PORT_KEY, 
Integer.toString(flumeServer.getPort())
-        )
-    ));
-
+    Tracer tracer = newTracer();
+    TraceCreator traceCreator = new TraceCreator(tracer);
     flumeServer.alwaysOk();
     traceCreator.createThreadedTrace();
   }
 
-  @Test
+  @Test(timeout=120000)
   public void testResilience() throws IOException {
-    traceCreator.addReceiver(new FlumeSpanReceiver(
-        HTraceConfiguration.fromKeyValuePairs(
-            FlumeSpanReceiver.FLUME_PORT_KEY, 
Integer.toString(flumeServer.getPort())
-        )
-    ));
-
+    Tracer tracer = newTracer();
+    TraceCreator traceCreator = new TraceCreator(tracer);
     flumeServer.alwaysFail();
     traceCreator.createThreadedTrace();
   }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java
----------------------------------------------------------------------
diff --git 
a/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java 
b/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java
deleted file mode 100644
index 12f36cb..0000000
--- a/htrace-hbase/src/main/java/org/apache/htrace/HBaseSpanReceiverHost.java
+++ /dev/null
@@ -1,107 +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;
-
-import java.io.IOException;
-import java.util.Collection;
-import java.util.HashSet;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.htrace.core.HTraceConfiguration;
-import org.apache.htrace.core.SpanReceiver;
-import org.apache.htrace.core.SpanReceiverBuilder;
-import org.apache.htrace.core.Trace;
-
-/**
- * This class provides functions for reading the names of SpanReceivers from
- * hbase-site.xml, adding those SpanReceivers to the Tracer, and closing those
- * SpanReceivers when appropriate.
- */
-public class HBaseSpanReceiverHost {
-  public static final String SPAN_RECEIVERS_CONF_KEY = 
"hbase.trace.spanreceiver.classes";
-  private static final Log LOG = 
LogFactory.getLog(HBaseSpanReceiverHost.class);
-  private Collection<SpanReceiver> receivers;
-  private Configuration conf;
-  private boolean closed = false;
-
-  private static enum SingletonHolder {
-    INSTANCE;
-    Object lock = new Object();
-    HBaseSpanReceiverHost host = null;
-  }
-
-  public static HBaseSpanReceiverHost getInstance(Configuration conf) {
-    synchronized (SingletonHolder.INSTANCE.lock) {
-      if (SingletonHolder.INSTANCE.host != null) {
-        return SingletonHolder.INSTANCE.host;
-      }
-
-      HBaseSpanReceiverHost host = new HBaseSpanReceiverHost(conf);
-      host.loadSpanReceivers();
-      SingletonHolder.INSTANCE.host = host;
-      return SingletonHolder.INSTANCE.host;
-    }
-
-  }
-
-  HBaseSpanReceiverHost(Configuration conf) {
-    receivers = new HashSet<SpanReceiver>();
-    this.conf = conf;
-  }
-
-  /**
-   * Reads the names of classes specified in the
-   * "hbase.trace.spanreceiver.classes" property and instantiates and registers
-   * them with the Tracer as SpanReceiver's.
-   *
-   */
-  public void loadSpanReceivers() {
-    String[] receiverNames = conf.getStrings(SPAN_RECEIVERS_CONF_KEY);
-    if (receiverNames == null || receiverNames.length == 0) {
-      return;
-    }
-    SpanReceiverBuilder builder = new SpanReceiverBuilder(new 
HBaseHTraceConfiguration(this.conf));
-    for (String className : receiverNames) {
-      SpanReceiver receiver = 
builder.spanReceiverClass(className.trim()).build();
-      if (receiver != null) {
-        receivers.add(receiver);
-        LOG.info("SpanReceiver " + className + " was loaded successfully.");
-      }
-    }
-    for (SpanReceiver rcvr : receivers) {
-      Trace.addReceiver(rcvr);
-    }
-  }
-
-  /**
-   * Calls close() on all SpanReceivers created by this HBaseSpanReceiverHost.
-   */
-  public synchronized void closeReceivers() {
-    if (closed) return;
-    closed = true;
-    for (SpanReceiver rcvr : receivers) {
-      try {
-        rcvr.close();
-      } catch (IOException e) {
-        LOG.warn("Unable to close SpanReceiver correctly: " + e.getMessage(), 
e);
-      }
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java 
b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
index 1f54f18..85de849 100644
--- a/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
+++ b/htrace-hbase/src/main/java/org/apache/htrace/impl/HBaseSpanReceiver.java
@@ -45,11 +45,10 @@ import org.apache.htrace.core.HTraceConfiguration;
 import org.apache.htrace.core.Sampler;
 import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanReceiver;
-import org.apache.htrace.core.SpanReceiverBuilder;
 import org.apache.htrace.core.TimelineAnnotation;
-import org.apache.htrace.core.Trace;
-import org.apache.htrace.core.TracerId;
 import org.apache.htrace.core.TraceScope;
+import org.apache.htrace.core.Tracer;
+import org.apache.htrace.core.TracerBuilder;
 import org.apache.htrace.protobuf.generated.SpanProtos;
 
 /**
@@ -59,7 +58,7 @@ import org.apache.htrace.protobuf.generated.SpanProtos;
  * From there background worker threads will send them
  * to a HBase database.
  */
-public class HBaseSpanReceiver implements SpanReceiver {
+public class HBaseSpanReceiver extends SpanReceiver {
   private static final Log LOG = LogFactory.getLog(HBaseSpanReceiver.class);
 
   public static final String COLLECTOR_QUORUM_KEY = 
"htrace.hbase.collector-quorum";
@@ -127,7 +126,6 @@ public class HBaseSpanReceiver implements SpanReceiver {
   private final byte[] cf;
   private final byte[] icf;
   private final int maxSpanBatchSize;
-  private final TracerId tracerId;
 
   public HBaseSpanReceiver(HTraceConfiguration conf) {
     this.queue = new ArrayBlockingQueue<Span>(1000);
@@ -155,7 +153,6 @@ public class HBaseSpanReceiver implements SpanReceiver {
     for (int i = 0; i < numThreads; i++) {
       this.service.submit(new WriteSpanRunnable());
     }
-    this.tracerId = new TracerId(conf);
   }
 
   private class WriteSpanRunnable implements Runnable {
@@ -334,9 +331,6 @@ public class HBaseSpanReceiver implements SpanReceiver {
   public void receiveSpan(Span span) {
     if (running.get()) {
       try {
-        if (span.getTracerId().isEmpty()) {
-          span.setTracerId(tracerId.get());
-        }
         this.queue.add(span);
       } catch (IllegalStateException e) {
         // todo: supress repeating error logs.
@@ -354,29 +348,28 @@ public class HBaseSpanReceiver implements SpanReceiver {
    * @throws IOException
    */
   public static void main(String[] args) throws Exception {
-    SpanReceiverBuilder builder =
-      new SpanReceiverBuilder(new 
HBaseHTraceConfiguration(HBaseConfiguration.create()));
-    SpanReceiver receiver =
-      builder.spanReceiverClass(HBaseSpanReceiver.class.getName()).build();
-    Trace.addReceiver(receiver);
-    TraceScope parent = Trace.startSpan("HBaseSpanReceiver.main.parent", 
Sampler.ALWAYS);
+    Tracer tracer = new TracerBuilder().
+        conf(new HBaseHTraceConfiguration(HBaseConfiguration.create())).
+        build();
+    tracer.addSampler(Sampler.ALWAYS);
+    TraceScope parent = tracer.newScope("HBaseSpanReceiver.main.parent");
     Thread.sleep(10);
     long traceid = parent.getSpan().getSpanId().getHigh();
-    TraceScope child1 = Trace.startSpan("HBaseSpanReceiver.main.child.1");
+    TraceScope child1 = tracer.newScope("HBaseSpanReceiver.main.child.1");
     Thread.sleep(10);
     child1.close();
-    TraceScope child2 = Trace.startSpan("HBaseSpanReceiver.main.child.2", 
parent.getSpan());
+    TraceScope child2 = tracer.newScope("HBaseSpanReceiver.main.child.2");
     Thread.sleep(10);
-    TraceScope gchild = Trace.startSpan("HBaseSpanReceiver.main.grandchild");
-    Trace.addTimelineAnnotation("annotation 1.");
+    TraceScope gchild = tracer.newScope("HBaseSpanReceiver.main.grandchild");
+    gchild.addTimelineAnnotation("annotation 1.");
     Thread.sleep(10);
-    Trace.addTimelineAnnotation("annotation 2.");
+    gchild.addTimelineAnnotation("annotation 2.");
     gchild.close();
     Thread.sleep(10);
     child2.close();
     Thread.sleep(10);
     parent.close();
-    receiver.close();
+    tracer.close();
     System.out.println("trace id: " + traceid);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
----------------------------------------------------------------------
diff --git 
a/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java 
b/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
index db7f6c6..d278c5e 100644
--- a/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
+++ b/htrace-hbase/src/test/java/org/apache/htrace/impl/HBaseTestUtil.java
@@ -27,7 +27,6 @@ import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.htrace.HBaseHTraceConfiguration;
 import org.apache.htrace.core.SpanReceiver;
-import org.apache.htrace.core.SpanReceiverBuilder;
 import org.junit.Assert;
 
 
@@ -59,7 +58,8 @@ public class HBaseTestUtil {
   }
 
   public static SpanReceiver startReceiver(Configuration conf) {
-    return new SpanReceiverBuilder(new HBaseHTraceConfiguration(conf)).build();
+    return new SpanReceiver.Builder(
+        new HBaseHTraceConfiguration(conf)).build();
   }
 
   public static SpanReceiver startReceiver(HBaseTestingUtility util) {

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java 
b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
index f1a3d19..253a873 100644
--- 
a/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
+++ 
b/htrace-hbase/src/test/java/org/apache/htrace/impl/TestHBaseSpanReceiver.java
@@ -30,20 +30,25 @@ import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.ResultScanner;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.client.Table;
 import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.htrace.core.HTraceConfiguration;
 import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanId;
-import org.apache.htrace.core.SpanReceiver;
 import org.apache.htrace.core.TimelineAnnotation;
 import org.apache.htrace.core.TraceCreator;
-import org.apache.htrace.core.TraceGraph.SpansByParent;
 import org.apache.htrace.core.TraceGraph;
+import org.apache.htrace.core.TraceGraph.SpansByParent;
+import org.apache.htrace.core.Tracer;
+import org.apache.htrace.core.TracerBuilder;
+import org.apache.htrace.core.TracerPool;
 import org.apache.htrace.protobuf.generated.SpanProtos;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -65,16 +70,39 @@ public class TestHBaseSpanReceiver {
     UTIL.shutdownMiniCluster();
   }
 
+  private Table createTable(HBaseTestingUtility util) {
+    Table htable = null;
+    try {
+      htable = util.createTable(Bytes.toBytes(HBaseSpanReceiver.DEFAULT_TABLE),
+          new byte[][]{Bytes.toBytes(HBaseSpanReceiver.DEFAULT_COLUMNFAMILY),
+              Bytes.toBytes(HBaseSpanReceiver.DEFAULT_INDEXFAMILY)});
+    } catch (IOException e) {
+      Assert.fail("failed to create htrace table. " + e.getMessage());
+    }
+    return htable;
+  }
+
   // Reenable after fix circular dependency
   @Ignore @Test
   public void testHBaseSpanReceiver() {
-    Table htable = HBaseTestUtil.createTable(UTIL);
-    SpanReceiver receiver = HBaseTestUtil.startReceiver(UTIL);
-    TraceCreator tc = new TraceCreator().addReceiver(receiver);
+    Table htable = createTable(UTIL);
+    Configuration conf = UTIL.getConfiguration();
+    Tracer tracer = new TracerBuilder().
+        name("testHBaseSpanReceiver").
+        tracerPool(new TracerPool("testHBaseSpanReceiver")).
+        conf(HTraceConfiguration.fromKeyValuePairs(
+          HBaseSpanReceiver.COLLECTOR_QUORUM_KEY,
+              conf.get(HConstants.ZOOKEEPER_QUORUM),
+          HBaseSpanReceiver.ZOOKEEPER_CLIENT_PORT_KEY,
+              "" + conf.getInt(HConstants.ZOOKEEPER_CLIENT_PORT, 2181),
+          HBaseSpanReceiver.ZOOKEEPER_ZNODE_PARENT_KEY,
+              conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT)
+        )).build();
+    TraceCreator tc = new TraceCreator(tracer);
     tc.createThreadedTrace();
     tc.createSimpleTrace();
     tc.createSampleRpcTrace();
-    HBaseTestUtil.stopReceiver(receiver);
+    tracer.close();
     Scan scan = new Scan();
     scan.addFamily(Bytes.toBytes(HBaseSpanReceiver.DEFAULT_COLUMNFAMILY));
     scan.setMaxVersions(1);

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java 
b/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java
index 77d0f49..1ac5126 100644
--- 
a/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java
+++ 
b/htrace-htraced/src/main/java/org/apache/htrace/impl/HTracedRESTReceiver.java
@@ -32,7 +32,6 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.htrace.core.HTraceConfiguration;
 import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanReceiver;
-import org.apache.htrace.core.TracerId;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.client.api.ContentResponse;
 import org.eclipse.jetty.client.api.Request;
@@ -65,7 +64,7 @@ import org.eclipse.jetty.http.HttpStatus;
  * TODO: Add lazy start; don't start background thread till a span gets queued.
  * TODO: Add some metrics; how many times we've run, how many spans and what 
size we've sent.
  */
-public class HTracedRESTReceiver implements SpanReceiver {
+public class HTracedRESTReceiver extends SpanReceiver {
   private static final Log LOG = LogFactory.getLog(HTracedRESTReceiver.class);
 
   /**
@@ -170,11 +169,6 @@ public class HTracedRESTReceiver implements SpanReceiver {
   private boolean mustStartFlush;
 
   /**
-   * The process ID to use for all spans.
-   */
-  private final TracerId tracerId;
-
-  /**
    * Create an HttpClient instance.
    *
    * @param connTimeout         The timeout to use for connecting.
@@ -226,7 +220,6 @@ public class HTracedRESTReceiver implements SpanReceiver {
             capacity + ", url=" + url +  ", periodInMs=" + periodInMs +
             ", maxToSendAtATime=" + maxToSendAtATime);
     }
-    tracerId = new TracerId(conf);
   }
 
   /**
@@ -322,7 +315,6 @@ public class HTracedRESTReceiver implements SpanReceiver {
       try {
         Request request = httpClient.newRequest(url).method(HttpMethod.POST);
         request.header(HttpHeader.CONTENT_TYPE, "application/json");
-        request.header("htrace-trid", tracerId.get());
         StringBuilder bld = new StringBuilder();
         for (Span span : spanBuf) {
           bld.append(span.toJson());

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java
 
b/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java
index 5e4bfc6..d52f071 100644
--- 
a/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java
+++ 
b/htrace-htraced/src/test/java/org/apache/htrace/impl/TestHTracedRESTReceiver.java
@@ -133,6 +133,8 @@ public class TestHTracedRESTReceiver {
           spanId(new SpanId(1L, i));
       if (i == NUM_SPANS - 1) {
         builder.tracerId("specialTrid");
+      } else {
+        builder.tracerId(TestHTraceConfiguration.TRACER_ID);
       }
       spans[i] = builder.build();
     }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
----------------------------------------------------------------------
diff --git 
a/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java 
b/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
index 6ba1afe..c106fa8 100644
--- a/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
+++ b/htrace-zipkin/src/main/java/org/apache/htrace/impl/ZipkinSpanReceiver.java
@@ -26,7 +26,6 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.htrace.core.HTraceConfiguration;
 import org.apache.htrace.core.Span;
 import org.apache.htrace.core.SpanReceiver;
-import org.apache.htrace.core.TracerId;
 import org.apache.htrace.zipkin.HTraceToZipkinConverter;
 import org.apache.thrift.protocol.TBinaryProtocol;
 import org.apache.thrift.protocol.TProtocol;
@@ -60,7 +59,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
  * HTrace spans are queued into a blocking queue.  From there background 
worker threads will
  * batch the spans together and then send them through to a Zipkin collector.
  */
-public class ZipkinSpanReceiver implements SpanReceiver {
+public class ZipkinSpanReceiver extends SpanReceiver {
   private static final Log LOG = LogFactory.getLog(ZipkinSpanReceiver.class);
 
   /**
@@ -141,8 +140,6 @@ public class ZipkinSpanReceiver implements SpanReceiver {
     }
   };
 
-  private final TracerId tracerId;
-
   ////////////////////
   /// Variables that will change on each call to configure()
   ///////////////////
@@ -155,7 +152,6 @@ public class ZipkinSpanReceiver implements SpanReceiver {
   public ZipkinSpanReceiver(HTraceConfiguration conf) {
     this.queue = new ArrayBlockingQueue<Span>(1000);
     this.protocolFactory = new TBinaryProtocol.Factory();
-    this.tracerId = new TracerId(conf);
     configure(conf);
   }
 
@@ -375,9 +371,6 @@ public class ZipkinSpanReceiver implements SpanReceiver {
   public void receiveSpan(Span span) {
     if (running.get()) {
       try {
-        if (span.getTracerId().isEmpty()) {
-          span.setTracerId(tracerId.get());
-        }
         this.queue.add(span);
       } catch (IllegalStateException e) {
         LOG.error("Error trying to append span (" + span.getDescription() + ") 
to the queue."

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
----------------------------------------------------------------------
diff --git 
a/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java 
b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
index 5b1ac3d..f166d35 100644
--- 
a/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
+++ 
b/htrace-zipkin/src/test/java/org/apache/htrace/TestHTraceSpanToZipkinSpan.java
@@ -20,12 +20,10 @@ package org.apache.htrace.zipkin;
 import com.twitter.zipkin.gen.zipkinCoreConstants;
 
 import org.apache.htrace.core.HTraceConfiguration;
-import org.apache.htrace.core.Span;
-import org.apache.htrace.core.SpanId;
-import org.apache.htrace.core.Trace;
 import org.apache.htrace.core.MilliSpan;
 import org.apache.htrace.core.POJOSpanReceiver;
-import org.apache.htrace.zipkin.HTraceToZipkinConverter;
+import org.apache.htrace.core.Span;
+import org.apache.htrace.core.SpanId;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -39,14 +37,10 @@ import static org.junit.Assert.assertTrue;
  * Creates HTrace and then convert it to Zipkin trace and checks whether it is 
a valid span or not.
  */
 public class TestHTraceSpanToZipkinSpan {
-
   private static final String ROOT_SPAN_DESC = "ROOT";
 
   @Test
   public void testHTraceToZipkin() throws IOException {
-    POJOSpanReceiver psr = new POJOSpanReceiver(HTraceConfiguration.EMPTY);
-    Trace.addReceiver(psr);
-
     Span rootSpan = new MilliSpan.Builder().
             description(ROOT_SPAN_DESC).
             parents(new SpanId[] { } ).

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/7997d208/src/main/site/markdown/index.md
----------------------------------------------------------------------
diff --git a/src/main/site/markdown/index.md b/src/main/site/markdown/index.md
index 6198472..e8e88fc 100644
--- a/src/main/site/markdown/index.md
+++ b/src/main/site/markdown/index.md
@@ -28,36 +28,50 @@ Before we get into the details, lets review our 
terminology.  HTrace
 borrows [Dapper's](http://research.google.com/pubs/pub36356.html)
 terminology.
 
-<b>Span:</b> The basic unit of work. For example, sending an RPC is a
-new span, as is sending a response to an RPC.
-Span's are identified by a unique 64-bit ID for the span and another
-64-bit ID for the trace the span is a part of.  Spans also have other
-data, such as descriptions, key-value annotations, the ID of the span
-that caused them, and process ID's (normally IP address).
+<b>Span:</b> The basic unit of work. For example, sending an RPC is a new span,
+as is sending a response to an RPC.  Spans are identified by a unique 128-bit
+ID.  Spans also have other data, such as descriptions, key-value annotations,
+the ID of the span that caused them, and tracer IDs.
 
 Spans are started and stopped, and they keep track of their timing
 information.  Once you create a span, you must stop it at some point
 in the future.
 
-<b>Trace:</b> A set of spans forming a tree-like structure.  For
-example, if you are running a distributed big-data store, a trace
-might be formed by a put request.
+<b>TracerId:</b> Identifies the Tracer object which created a specific Span.
+The TracerId should identify the source of the Span.  "10.0.0.2/DataNode" is a
+typical TracerID.
+
+<b>SpanReceiver:</b> SpanReceivers handle spans once they have been created.
+Typically, Span Receivers send spans to an external data store to be
+analyzed.  HTrace comes with several standard span receivers, such as
+`LocalFileSpanReceiver`.
+
+<b>Sampler:</b> Samplers determine when tracing should be enabled, and when it
+should be disabled.   The goal of HTrace is to trace an entire request.
 
 ### How to add tracing to your application
 To instrument your system you must:
 
-<b>1. Attach additional information to your RPC's.</b>
-In order to create the causal links necessary for a trace, HTrace
-needs to know about the causal
-relationships between spans.  The only information you need to add to
-your RPC's is two 64-bit longs.  If tracing is enabled (Trace.isTracing()
-returns true) when you send an RPC, attach the ID of the current span
-and the ID of the current trace to the message.
-On the receiving end of the RPC, check to see if the message has the
-additional tracing information above.  If it does, start a new span
-with the information given (more on that in a bit).
-
-<b>2. Wrap your thread changes.</b>
+<b>1. Create a Tracer object.</b>
+You can create a Tracer object via the TracerBuilder.
+
+````java
+    Tracer tracer = new TracerBuilder(conf).setName("MyApp").build();
+...
+
+The TracerBuilder will take care of creating the appropriate Sampler and
+SpanReceiver objects, as well as the Tracer itself.   If a SpanReceiver was
+created, we will install a shutdown hook to close it when the JVM shuts down.
+
+<b>2. Attach additional information to your RPCs.</b>
+In order to create the causal links necessary for a trace, HTrace needs to know
+about the causal relationships between spans.  The only information you need to
+add to your RPCs is the 128-bit span ID.  If tracing is enabled when you send 
an
+RPC, attach the ID of the current span to the message.  On the receiving end of
+the RPC, check to see if the message has a span ID attached.  If it does, start
+a new trace scope with that span as a parent.
+
+<b>3. Wrap your thread changes.</b>
 HTrace stores span information in java's ThreadLocals, which causes
 the trace to be "lost" on thread changes. The only way to prevent
 this is to "wrap" your thread changes. For example, if your code looks
@@ -104,7 +118,7 @@ the computation that you then stop after the computation has
 finished. It might look like this:
 
 ````java
-    Span computationSpan = Trace.startSpan("Expensive computation.");
+    Span computationSpan = tracer.newScope("Expensive computation.");
     try {
         //expensive computation here
     } finally {
@@ -117,110 +131,10 @@ HTrace also supports key-value annotations on a 
per-trace basis.
 Example:
 
 ````java
-    Trace.currentTrace().addAnnotation("faultyRecordCounter".getBytes(), 
"1".getBytes());
+    scope.addAnnotation("faultyRecordCounter".getBytes(), "1".getBytes());
 ````
 
-`Trace.currentTrace()` will not return `null` if the current thread is
-not tracing, but instead it will return a `NullSpan`, which does
-nothing on any of its method calls. The takeaway here is you can call
-methods on the `currentTrace()` without fear of NullPointerExceptions.
-
-###Samplers
-`Sampler` is an interface that defines one function:
-
-````java
-    boolean next(T info);
-````
-
-All of the `Trace.startSpan()` methods can take an optional sampler.
-A new span is only created if the sampler's next function returns
-true.  If the Sampler returns false, the `NullSpan` is returned from
-`startSpan()`, so it's safe to call `stop()` or `addAnnotation()` on it.
-As you may have noticed from the `next()` method signature, Sampler is
-parameterized.  The argument to `next()` is whatever piece of
-information you might need for sampling.  See `Sampler.java` for an
-example of this.  If you do not require any additional information,
-then just ignore the parameter.
-HTrace includes  a sampler that always returns true, a
-sampler that always returns false and a sampler returns true some
-percentage of the time (you pass in the percentage as a decimal at 
construction).
-
-HTrace comes with several standard samplers, including `AlwaysSampler`,
-`NeverSampler`, `ProbabilitySampler`, and `CountSampler`.  An application can
-use the `SamplerBuilder` to create one of these standard samplers based on the
-current configuration.
-
-````java
-  HTraceConfiguration hconf = createMyHTraceConfiguration();
-  Sampler sampler = new SamplerBuilder(hconf).build();
-````
-
-####Trace.startSpan()
-There is a single method to create and start spans: `startSpan()`.
-For the `startSpan()` methods that do not take an explicit Sampler, the
-default Sampler is used.  The default sampler returns true if and only
-if tracing is already on in the current thread.  That means that
-calling `startSpan()` with no explicit Sampler is a good idea when you
-have information that you would like to add to a trace if it's already
-occurring, but is not something you would want to start a whole new
-trace for.
-
-If you are using a sampler that makes use of the `T info` parameter to
-`next()`, just pass in the object as the last argument.  If you leave it
-out, HTrace will pass `null` for you (so make sure your Samplers can
-handle `null`).
-
-Aside from whether or not you pass in an explicit `Sampler`, there are
-other options you have when calling `startSpan()`.
-For the next section I am assuming you are familiar with the options
-for passing in `Samplers` and `info` parameters, so when I say "no
-arguments," I mean no additional arguments other than whatever
-`Sampler`/`info` parameters you deem necessary.
-
-You can call `startSpan()` with no additional arguments.
-In this case, `Trace.java` will start a span if the sampler (explicit
-or default) returns true. If the current span is not the `NullSpan`, the span
-returned will be a child of the current span, otherwise it will start
-a new trace in the current thread (it will be a
-`ProcessRootMilliSpan`). All of the other `startSpan()` methods take some
-parameter describing the parent span of the span to be created. The
-version that takes a parent id will mostly be used when continuing a trace over
-RPC. The receiver of the RPC will check the message for the 128-bit parent 
trace
-ID and will call `startSpan()` if it is attached.  The last `startSpan()` takes
-a `Span parent`.  The result of `parent.child()` will be used for the new span.
-`Span.child()` simply returns a span that is a child of `this`.
-
-###Span Receivers
-In order to use the tracing information consisting of spans,
-you need an implementation of `SpanReceiver` interface which collects spans
-and typically writes it to files or databases or collector services.
-The `SpanReceiver` implementation must provide a `receiveSpan` method which
-is called from `Trace.deliver` method.
-You do not need to explicitly call `Trace.deliver`
-because it is internally called by the implementation of `Span`.
-
-````java
-    public interface SpanReceiver extends Closeable {
-      public void receiveSpan(Span span);
-    }
-````
-
-HTrace comes with several standard span receivers, such as
-`LocalFileSpanReceiver`.  An application can use the `SpanReceiverBuilder` to
-create a particular type of standard `SpanReceiver` based on the current
-configuration.  Once a SpanReceiver has been created, it should be registered
-with the HTrace framework by calling `Trace.addReceiver`.
-
-````java
-    HTraceConfiguration hconf = createMyHTraceConfiguration();
-    SpanReceiverBuilder builder = new SpanReceiverBuilder(hconf);
-    SpanReceiver spanReceiver = builder.build();
-    if (spanReceiver != null) {
-      Trace.addReceiver(spanReceiver);
-    }
-````
-
-####Zipkin
+#### htrace-zipkin
 htrace-zipkin provides the `SpanReceiver` implementation
 which sends spans to [Zipkin](https://github.com/twitter/zipkin) collector.
 You can build the uber-jar (htrace-zipkin-*-jar-withdependency.jar) for manual
@@ -230,34 +144,16 @@ htrace-core and its dependencies.
     $ cd htrace-zipkin
     $ mvn compile assembly:single
 
-####HBase Receiver
+#### htrace-hbase
 See htrace-hbase for an Span Receiver implementation that writes HBase.
-Also bundled is a simple Span Viewer.
 
-Testing Information
+Generating test spans
 -------------------------------
-
 The test that creates a sample trace (TestHTrace) takes a command line
 argument telling it where to write span information. Run
 `mvn test -DargLine="-DspanFile=FILE\_PATH"` to write span
 information to FILE_PATH. If no file is specified, span information
-will be written to standard out. If span information is written to a
-file, you can use the included graphDrawer python script in tools/
-to create a simple visualization of the trace. Or you could write
-some javascript to make a better visualization, and send a pull
-request if you do :).
-
-Migrating to Apache HTrace (incubating) from org.htrace
--------------------------------
-First, the package has changed from org.htrace to org.apache.htrace.
-
-Also, here are some notes on what had to be done migrating:
-
-HTRACE-1 changes the SpanReceiver interface. HBase was instantiating
-SpanReceivers itself and calling .configure(HTraceConfiguration) on each
-one. The expectation now is that SpanReceiver implementations
-provide a constructor that takes a single parameter of HTraceConfiguration.
-
+will be written to standard out.
 
 Publishing to Maven Central
 -------------------------------


Reply via email to