Redo API to use a TraceScope context

This allows a span to be passed around between threads more easily
diff --git a/src/main/java/org/cloudera/htrace/Span.java b/src/main/java/org/cloudera/htrace/Span.java
index 54eaf29..c5d6e63 100644
--- a/src/main/java/org/cloudera/htrace/Span.java
+++ b/src/main/java/org/cloudera/htrace/Span.java
@@ -54,9 +54,6 @@
   /** A pseudo-unique (random) number assigned to this span instance */
   long getSpanId();
 
-  /** The parent span: returns the NullSpan if this is the root span */
-  Span getParent();
-
   /**
    * A pseudo-unique (random) number assigned to the trace associated with this
    * span
diff --git a/src/main/java/org/cloudera/htrace/Trace.java b/src/main/java/org/cloudera/htrace/Trace.java
index 19f7774..80066b0 100644
--- a/src/main/java/org/cloudera/htrace/Trace.java
+++ b/src/main/java/org/cloudera/htrace/Trace.java
@@ -20,8 +20,7 @@
 import java.util.Random;
 import java.util.concurrent.Callable;
 
-import org.cloudera.htrace.impl.NullSpan;
-import org.cloudera.htrace.impl.ProcessRootMilliSpan;
+import org.cloudera.htrace.impl.MilliSpan;
 import org.cloudera.htrace.impl.TrueIfTracingSampler;
 import org.cloudera.htrace.wrappers.TraceCallable;
 import org.cloudera.htrace.wrappers.TraceRunnable;
@@ -42,7 +41,7 @@
    *          Description of the span to be created.
    * @return
    */
-  public static Span startSpan(String description) {
+  public static TraceScope startSpan(String description) {
     return startSpan(description, TrueIfTracingSampler.INSTANCE);
   }
 
@@ -58,32 +57,37 @@
    *          be returned.
    * @return
    */
-  public static Span startSpan(String description, Span parent) {
-    return Tracer.getInstance().setCurrentSpan(parent.child(description));
+  public static TraceScope startSpan(String description, Span parent) {
+    return continueSpan(parent.child(description));
   }
 
-  public static Span startSpan(String description, TraceInfo tinfo) {
-    return Tracer.getInstance().setCurrentSpan(
-        new ProcessRootMilliSpan(description, tinfo.traceId, random.nextLong(),
-            tinfo.parentSpanId, Tracer.getProcessId()));
+  public static TraceScope startSpan(String description, TraceInfo tinfo) {
+    if (tinfo == null) return continueSpan(null);
+    Span newSpan = new MilliSpan(description, tinfo.traceId, tinfo.spanId,
+        random.nextLong(), Tracer.getProcessId());
+    return continueSpan(newSpan);
   }
   
-  public static <T> Span startSpan(String description, Sampler<T> s) {
+  public static <T> TraceScope startSpan(String description, Sampler<T> s) {
     return startSpan(description, s, null);
   }
 
-  public static <T> Span startSpan(String description, Sampler<T> s, T info) {
+  public static <T> TraceScope startSpan(String description, Sampler<T> s, T info) {
+    Span span = null;
     if (isTracing() || s.next(info)) {
-      return Tracer.getInstance().on(description);
+      span = Tracer.getInstance().createNew(description);
     }
-    return NullSpan.getInstance();
+    return continueSpan(span);
   }
   
   /**
    * Pick up an existing span from another thread.
    */
-  public static void continueSpan(Span s) {
-    Tracer.getInstance().setCurrentSpan(s);
+  public static TraceScope continueSpan(Span s) {
+    // Return an empty TraceScope that does nothing on
+    // close
+    if (s == null) return new TraceScope(null, null);
+    return Tracer.getInstance().continueSpan(s);
   }
   
   /**
@@ -129,36 +133,14 @@
    * Adds a data annotation to the current span if tracing is currently on.
    */
   public static void addKVAnnotation(byte[] key, byte[] value) {
-    currentTrace().addKVAnnotation(key, value);
+    currentSpan().addKVAnnotation(key, value);
   }
   
   /**
    * Annotate the current span with the given message.
    */
   public static void addTimelineAnnotation(String msg) {
-    currentTrace().addTimelineAnnotation(msg);
-  }
-
-  /**
-   * Provides the current trace's span ID and parent ID, or the TInfo: (0,0) if
-   * tracing is currently off.
-   *
-   * @return TINfo current trace or the sentinel no-trace (0,0) if tracing off
-   */
-  public static TraceInfo traceInfo() {
-    return Tracer.traceInfo();
-  }
-
-  /**
-   * If 'span' is not null, it is delivered to any registered SpanReceiver's,
-   * and sets the current span to 'span's' parent. If 'span' is null, the
-   * current trace is set to null.
-   *
-   * @param span
-   *          The Span to be popped
-   */
-  public static void pop(Span span) {
-    Tracer.getInstance().pop(span);
+    currentSpan().addTimelineAnnotation(msg);
   }
 
   /**
@@ -175,7 +157,7 @@
    *
    * @return Span representing the current trace, or null if not tracing.
    */
-  public static Span currentTrace() {
+  public static Span currentSpan() {
     return Tracer.getInstance().currentTrace();
   }
 
@@ -187,7 +169,7 @@
    */
   public static <V> Callable<V> wrap(Callable<V> callable) {
     if (isTracing()) {
-      return new TraceCallable<V>(Trace.currentTrace(), callable);
+      return new TraceCallable<V>(Trace.currentSpan(), callable);
     } else {
       return callable;
     }
@@ -201,7 +183,7 @@
    */
   public static Runnable wrap(Runnable runnable) {
     if (isTracing()) {
-      return new TraceRunnable(Trace.currentTrace(), runnable);
+      return new TraceRunnable(Trace.currentSpan(), runnable);
     } else {
       return runnable;
     }
diff --git a/src/main/java/org/cloudera/htrace/TraceInfo.java b/src/main/java/org/cloudera/htrace/TraceInfo.java
index 1db6989..0b90ac2 100644
--- a/src/main/java/org/cloudera/htrace/TraceInfo.java
+++ b/src/main/java/org/cloudera/htrace/TraceInfo.java
@@ -19,15 +19,20 @@
 
 public class TraceInfo {
   public final long traceId;
-  public final long parentSpanId;
+  public final long spanId;
 
-  public TraceInfo(long traceId, long parentSpanId) {
+  public TraceInfo(long traceId, long spanId) {
     this.traceId = traceId;
-    this.parentSpanId = parentSpanId;
+    this.spanId = spanId;
   }
 
   @Override
   public String toString() {
-    return "TraceInfo(traceId=" + traceId + ", parentSpanId=" + parentSpanId + ")";
+    return "TraceInfo(traceId=" + traceId + ", spanId=" + spanId + ")";
+  }
+
+  public static TraceInfo fromSpan(Span s) {
+    if (s == null) return null;
+    return new TraceInfo(s.getTraceId(), s.getSpanId());
   }
 }
diff --git a/src/main/java/org/cloudera/htrace/TraceScope.java b/src/main/java/org/cloudera/htrace/TraceScope.java
new file mode 100644
index 0000000..6fd8830
--- /dev/null
+++ b/src/main/java/org/cloudera/htrace/TraceScope.java
@@ -0,0 +1,55 @@
+package org.cloudera.htrace;
+
+import java.io.Closeable;
+
+public class TraceScope implements Closeable {
+
+  /** the span for this scope */
+  private final Span span;
+
+  /** the span that was "current" before this scope was entered */
+  private final Span savedSpan;
+
+  private boolean detached = false;
+  
+  TraceScope(Span span, Span saved) {
+    this.span = span;
+    this.savedSpan = saved;
+  }
+  
+  public Span getSpan() {
+    return span;
+  }
+  
+  /**
+   * Remove this span as the current thread, but don't stop it yet or
+   * send it for collection. This is useful if the span object is then
+   * passed to another thread for use with Trace.continueTrace().
+   * 
+   * @return the same Span object
+   */
+  public Span detach() {
+    detached = true;
+
+    Span cur = Tracer.getInstance().currentTrace();
+    if (cur != span) {
+      Tracer.LOG.debug("Closing trace span " + span + " but " +
+        cur + " was top-of-stack");
+    } else {
+      Tracer.getInstance().setCurrentSpan(savedSpan);
+    }
+    return span;
+  }
+  
+  @Override
+  public void close() {
+    if (span == null) return;
+    
+    if (!detached) {
+      // The span is done
+      span.stop();
+      Tracer.getInstance().deliver(span);
+      detach();
+    }
+  }
+}
diff --git a/src/main/java/org/cloudera/htrace/Tracer.java b/src/main/java/org/cloudera/htrace/Tracer.java
index a303e55..06e3d9d 100644
--- a/src/main/java/org/cloudera/htrace/Tracer.java
+++ b/src/main/java/org/cloudera/htrace/Tracer.java
@@ -24,8 +24,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.cloudera.htrace.impl.NullSpan;
-import org.cloudera.htrace.impl.ProcessRootMilliSpan;
+import org.cloudera.htrace.impl.MilliSpan;
 
 /**
  * A Tracer provides the implementation for collecting and distributing Spans
@@ -38,7 +37,7 @@
   private static final ThreadLocal<Span> currentTrace = new ThreadLocal<Span>() {
     @Override
     protected Span initialValue() {
-      return NullSpan.getInstance();
+      return null;
     }
   };
   public static final TraceInfo DONT_TRACE = new TraceInfo(-1, -1);
@@ -53,28 +52,21 @@
     return instance;
   }
 
-  protected static TraceInfo traceInfo() {
-    Span span = currentTrace.get();
-    if (!span.equals(NullSpan.getInstance())) {
-      return new TraceInfo(span.getTraceId(), span.getSpanId());
-    }
-    return DONT_TRACE;
-  }
-
-  protected Span on(String description) {
+  protected Span createNew(String description) {
     Span parent = currentTrace.get();
-    Span root;
-    if (parent.equals(NullSpan.getInstance())) {
-      root = new ProcessRootMilliSpan(description, random.nextLong(),
-          random.nextLong(), Span.ROOT_SPAN_ID, getProcessId());
+    if (parent == null) {
+      return new MilliSpan(description,
+          /* traceId = */ random.nextLong(),
+          /* parentSpanId = */ Span.ROOT_SPAN_ID,
+          /* spanId = */ random.nextLong(),
+          getProcessId());
     } else {
-      root = parent.child(description);
+      return parent.child(description);
     }
-    return setCurrentSpan(root);
   }
 
   protected boolean isTracing() {
-    return !currentTrace.get().equals(NullSpan.getInstance());
+    return currentTrace.get() != null;
   }
 
   protected Span currentTrace() {
@@ -96,34 +88,24 @@
   }
 
   protected Span setCurrentSpan(Span span) {
-    if (span != null) {
-      currentTrace.set(span);
-    }
+    currentTrace.set(span);
     return span;
   }
   
+
+  public TraceScope continueSpan(Span s) {
+    Span oldCurrent = currentTrace();
+    setCurrentSpan(s);
+    return new TraceScope(s, oldCurrent);
+  }
+
+  
   protected void clearTraceStack() {
     while (isTracing()) {
       currentTrace().stop();
     }
   }
 
-  protected void pop(Span span) {
-    if (span != null) {
-      if (!span.equals(currentTrace())
-          && !currentTrace().equals(NullSpan.getInstance())) {
-        LOG.warn("Stopped span: " + span
-            + " that was not the current span. Current span is: "
-            + currentTrace());
-      }
-      deliver(span);
-      Span parent = span.getParent();
-      currentTrace.set(parent != null ? parent : NullSpan.getInstance());
-    } else {
-      currentTrace.set(NullSpan.getInstance());
-    }
-  }
-
   protected int numReceivers() {
     return receivers.size();
   }
diff --git a/src/main/java/org/cloudera/htrace/impl/MilliSpan.java b/src/main/java/org/cloudera/htrace/impl/MilliSpan.java
index 6360abf..396627e 100644
--- a/src/main/java/org/cloudera/htrace/impl/MilliSpan.java
+++ b/src/main/java/org/cloudera/htrace/impl/MilliSpan.java
@@ -16,7 +16,6 @@
  */
 package org.cloudera.htrace.impl;
 
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -26,7 +25,6 @@
 
 import org.cloudera.htrace.Span;
 import org.cloudera.htrace.TimelineAnnotation;
-import org.cloudera.htrace.Trace;
 
 /**
  * A Span implementation that stores its information in milliseconds since the
@@ -34,11 +32,13 @@
  */
 public class MilliSpan implements Span {
 
-  private static final Random next = new SecureRandom();
+  private static Random rand = new Random();
+  
   private long start;
   private long stop;
-  private final Span parent;
   private final String description;
+  private final long traceId;
+  private final long parentSpanId;
   private final long spanId;
   private Map<byte[], byte[]> traceInfo = null;
   private final String processId;
@@ -46,13 +46,14 @@
 
   @Override
   public Span child(String description) {
-    return new MilliSpan(description, next.nextLong(), this, processId);
+    return new MilliSpan(description, traceId, spanId, rand.nextLong(), processId);
   }
 
-  public MilliSpan(String description, long id, Span parent, String processId) {
+  public MilliSpan(String description, long traceId, long parentSpanId, long spanId, String processId) {
     this.description = description;
-    this.spanId = id;
-    this.parent = parent;
+    this.traceId = traceId;
+    this.parentSpanId = parentSpanId;
+    this.spanId = spanId;
     this.start = System.currentTimeMillis();
     this.stop = 0;
     this.processId = processId;
@@ -64,9 +65,8 @@
       throw new IllegalStateException("Span for " + description
           + " has not been started");
     stop = System.currentTimeMillis();
-    Trace.pop(this);
   }
-
+  
   protected long currentTimeMillis() {
     return System.currentTimeMillis();
   }
@@ -87,7 +87,7 @@
 
   @Override
   public String toString() {
-    return "start=" + start + "\nstop=" + stop + "\nparent=" + parent
+    return "start=" + start + "\nstop=" + stop + "\nparentId=" + parentSpanId
         + "\ndescription=" + description + "\nspanId=" + spanId
         + "\ntraceInfo=" + traceInfo + "\nprocessId=" + processId
         + "\ntimeline=" + timeline;
@@ -104,20 +104,13 @@
   }
 
   @Override
-  public Span getParent() {
-    return parent;
-  }
-
-  @Override
   public long getParentId() {
-    if (parent == null)
-      return -1;
-    return parent.getSpanId();
+    return parentSpanId;
   }
 
   @Override
   public long getTraceId() {
-    return parent.getTraceId();
+    return traceId;
   }
 
   @Override
diff --git a/src/main/java/org/cloudera/htrace/impl/NullSpan.java b/src/main/java/org/cloudera/htrace/impl/NullSpan.java
deleted file mode 100644
index 7fdea6a..0000000
--- a/src/main/java/org/cloudera/htrace/impl/NullSpan.java
+++ /dev/null
@@ -1,125 +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.cloudera.htrace.impl;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import org.cloudera.htrace.Span;
-import org.cloudera.htrace.TimelineAnnotation;
-
-/**
- * A Span that does nothing. Used to avoid returning and checking for nulls when
- * we are not tracing.
- * 
- */
-public class NullSpan implements Span {
-
-  private static NullSpan instance = new NullSpan();
-
-  // No need to ever have more than one NullSpan.
-  public static NullSpan getInstance() {
-    return instance;
-  }
-
-  private NullSpan() {
-  }
-
-  @Override
-  public long getAccumulatedMillis() {
-    return 0;
-  }
-
-  @Override
-  public String getDescription() {
-    return "NullSpan";
-  }
-
-  @Override
-  public long getStartTimeMillis() {
-    return 0;
-  }
-
-  @Override
-  public long getStopTimeMillis() {
-    return 0;
-  }
-
-  @Override
-  public Span getParent() {
-    return null;
-  }
-
-  @Override
-  public long getParentId() {
-    return -1;
-  }
-
-  @Override
-  public boolean isRunning() {
-    return false;
-  }
-
-  @Override
-  public long getSpanId() {
-    return -1;
-  }
-
-  @Override
-  public void stop() {
-  }
-
-  @Override
-  public long getTraceId() {
-    return -1;
-  }
-
-  @Override
-  public Span child(String description) {
-    return this;
-  }
-
-  @Override
-  public void addKVAnnotation(byte[] key, byte[] value) {
-  }
-  
-  @Override
-  public void addTimelineAnnotation(String msg) {
-  }
-
-  @Override
-  public String toString() {
-    return "Not Tracing";
-  }
-
-  @Override
-  public Map<byte[], byte[]> getKVAnnotations() {
-    return Collections.emptyMap();
-  }
-
-  @Override
-  public List<TimelineAnnotation> getTimelineAnnotations() {
-    return Collections.emptyList();
-  }
-
-  @Override
-  public String getProcessId() {
-    return "";
-  }
-
-}
diff --git a/src/main/java/org/cloudera/htrace/impl/ProcessRootMilliSpan.java b/src/main/java/org/cloudera/htrace/impl/ProcessRootMilliSpan.java
deleted file mode 100644
index 1a63b0e..0000000
--- a/src/main/java/org/cloudera/htrace/impl/ProcessRootMilliSpan.java
+++ /dev/null
@@ -1,46 +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.cloudera.htrace.impl;
-
-
-/**
- * Span that roots the span tree in a process, but perhaps not the whole trace.
- * 
- */
-public class ProcessRootMilliSpan extends MilliSpan {
-
-  private final long traceId;
-  private final long parentId;
- 
-  @Override
-  public long getTraceId() {
-    return traceId;
-  }
-
-  public ProcessRootMilliSpan(String description, long traceId, long spanId,
-      long parentId,
-      String processId) {
-    super(description, spanId, NullSpan.getInstance(), processId);
-    this.traceId = traceId;
-    this.parentId = parentId;
-  }
-
-  @Override
-  public long getParentId() {
-    return parentId;
-  }
-}
diff --git a/src/main/java/org/cloudera/htrace/wrappers/TraceCallable.java b/src/main/java/org/cloudera/htrace/wrappers/TraceCallable.java
index a25b416..5a58e74 100644
--- a/src/main/java/org/cloudera/htrace/wrappers/TraceCallable.java
+++ b/src/main/java/org/cloudera/htrace/wrappers/TraceCallable.java
@@ -20,6 +20,7 @@
 
 import org.cloudera.htrace.Span;
 import org.cloudera.htrace.Trace;
+import org.cloudera.htrace.TraceScope;
 
 /**
  * Wrap a Callable with a Span that survives a change in threads.
@@ -30,7 +31,7 @@
   private final Span parent;
 
   public TraceCallable(Callable<V> impl) {
-    this(Trace.currentTrace(), impl);
+    this(Trace.currentSpan(), impl);
   }
 
   public TraceCallable(Span parent, Callable<V> impl) {
@@ -41,12 +42,12 @@
   @Override
   public V call() throws Exception {
     if (parent != null) {
-      Span chunk = Trace.startSpan(Thread.currentThread().getName(), parent);
+      TraceScope chunk = Trace.startSpan(Thread.currentThread().getName(), parent);
 
       try {
         return impl.call();
       } finally {
-        chunk.stop();
+        chunk.close();
       }
     } else {
       return impl.call();
diff --git a/src/main/java/org/cloudera/htrace/wrappers/TraceProxy.java b/src/main/java/org/cloudera/htrace/wrappers/TraceProxy.java
index fdf6f9d..92118b3 100644
--- a/src/main/java/org/cloudera/htrace/wrappers/TraceProxy.java
+++ b/src/main/java/org/cloudera/htrace/wrappers/TraceProxy.java
@@ -23,6 +23,7 @@
 import org.cloudera.htrace.Sampler;
 import org.cloudera.htrace.Span;
 import org.cloudera.htrace.Trace;
+import org.cloudera.htrace.TraceScope;
 
 public class TraceProxy {
   /**
@@ -54,14 +55,14 @@
           return method.invoke(instance, args);
         }
 
-        Span span = Trace.startSpan(method.getName(), Sampler.ALWAYS);
+        TraceScope scope = Trace.startSpan(method.getName(), Sampler.ALWAYS);
         try {
           return method.invoke(instance, args);
         } catch (Throwable ex) {
           ex.printStackTrace();
           throw ex;
         } finally {
-          span.stop();
+          scope.close();
         }
       }
     };
diff --git a/src/main/java/org/cloudera/htrace/wrappers/TraceRunnable.java b/src/main/java/org/cloudera/htrace/wrappers/TraceRunnable.java
index 122c227..fdfa895 100644
--- a/src/main/java/org/cloudera/htrace/wrappers/TraceRunnable.java
+++ b/src/main/java/org/cloudera/htrace/wrappers/TraceRunnable.java
@@ -19,6 +19,7 @@
 import org.cloudera.htrace.Sampler;
 import org.cloudera.htrace.Span;
 import org.cloudera.htrace.Trace;
+import org.cloudera.htrace.TraceScope;
 
 /**
  * Wrap a Runnable with a Span that survives a change in threads.
@@ -30,7 +31,7 @@
   private final Runnable runnable;
 
   public TraceRunnable(Runnable runnable) {
-    this(Trace.currentTrace(), runnable);
+    this(Trace.currentSpan(), runnable);
   }
 
   public TraceRunnable(Span parent, Runnable runnable) {
@@ -41,12 +42,12 @@
   @Override
   public void run() {
     if (parent != null) {
-      Span chunk = Trace.startSpan(Thread.currentThread().getName(), parent);
+      TraceScope chunk = Trace.startSpan(Thread.currentThread().getName(), parent);
 
       try {
         runnable.run();
       } finally {
-        chunk.stop();
+        chunk.close();
       }
     } else {
       runnable.run();
diff --git a/src/test/java/org/cloudera/htrace/TestSampler.java b/src/test/java/org/cloudera/htrace/TestSampler.java
index be378e4..e3b9a7d 100644
--- a/src/test/java/org/cloudera/htrace/TestSampler.java
+++ b/src/test/java/org/cloudera/htrace/TestSampler.java
@@ -1,28 +1,26 @@
 package org.cloudera.htrace;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
-import org.cloudera.htrace.impl.NullSpan;
 import org.junit.Test;
 
 public class TestSampler {
   @Test
   public void testParamterizedSampler() {
     TestParamSampler sampler = new TestParamSampler();
-    Span s = Trace.startSpan("test", sampler, 1);
-    assertFalse(s.equals(NullSpan.getInstance()));
-    s.stop();
+    TraceScope s = Trace.startSpan("test", sampler, 1);
+    assertNotNull(s.getSpan());
+    s.close();
     s = Trace.startSpan("test", sampler, -1);
-    assertTrue(s.equals(NullSpan.getInstance()));
-    s.stop();
+    assertNull(s.getSpan());
+    s.close();
   }
 
   @Test
   public void testAlwaysSampler() {
-    Span cur = Trace.startSpan("test", new TraceInfo(0, 0));
-    assertFalse(cur.equals(NullSpan.getInstance()));
-    cur.stop();
+    TraceScope cur = Trace.startSpan("test", new TraceInfo(0, 0));
+    assertNotNull(cur);
+    cur.close();
   }
 
   private class TestParamSampler implements Sampler<Integer> {
diff --git a/src/test/java/org/cloudera/htrace/TraceCreator.java b/src/test/java/org/cloudera/htrace/TraceCreator.java
index 48fe77c..34c38d1 100644
--- a/src/test/java/org/cloudera/htrace/TraceCreator.java
+++ b/src/test/java/org/cloudera/htrace/TraceCreator.java
@@ -51,21 +51,20 @@
   }
 
   public void createSampleRpcTrace() {
-    Span s = Trace.startSpan(RPC_TRACE_ROOT, Sampler.ALWAYS);
+    TraceScope s = Trace.startSpan(RPC_TRACE_ROOT, Sampler.ALWAYS);
     try {
       pretendRpcSend();
     } finally {
-      // This will produce a warning from Tracer.java that is expected.
-      s.stop();
+      s.close();
     }
   }
 
   public void createSimpleTrace() {
-    Span s = Trace.startSpan(SIMPLE_TRACE_ROOT, Sampler.ALWAYS);
+    TraceScope s = Trace.startSpan(SIMPLE_TRACE_ROOT, Sampler.ALWAYS);
     try {
       importantWork1();
     } finally {
-      s.stop();
+      s.close();
     }
   }
 
@@ -73,7 +72,7 @@
    * Creates the demo trace (will create different traces from call to call).
    */
   public void createThreadedTrace() {
-    Span s = Trace.startSpan(THREADED_TRACE_ROOT, Sampler.ALWAYS);
+    TraceScope s = Trace.startSpan(THREADED_TRACE_ROOT, Sampler.ALWAYS);
     try {
       Random r = new Random();
       int numThreads = r.nextInt(4) + 1;
@@ -93,30 +92,30 @@
       }
       importantWork1();
     } finally {
-      s.stop();
+      s.close();
     }
   }
 
   private void importantWork1() {
-    Span cur = Trace.startSpan("important work 1");
+    TraceScope cur = Trace.startSpan("important work 1");
     try {
       Thread.sleep((long) (2000 * Math.random()));
       importantWork2();
     } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
     } finally {
-      cur.stop();
+      cur.close();
     }
   }
 
   private void importantWork2() {
-    Span cur = Trace.startSpan("important work 2");
+    TraceScope cur = Trace.startSpan("important work 2");
     try {
       Thread.sleep((long) (2000 * Math.random()));
     } catch (InterruptedException e) {
       Thread.currentThread().interrupt();
     } finally {
-      cur.stop();
+      cur.close();
     }
   }
 
@@ -131,28 +130,28 @@
       } catch (InterruptedException ie) {
         Thread.currentThread().interrupt();
       } catch (ArithmeticException ae) {
-        Span c = Trace.startSpan("dealing with arithmetic exception.");
+        TraceScope c = Trace.startSpan("dealing with arithmetic exception.");
         try {
           Thread.sleep((long) (3000 * Math.random()));
         } catch (InterruptedException ie1) {
           Thread.currentThread().interrupt();
         } finally {
-          c.stop();
+          c.close();
         }
       }
     }
   }
 
   public void pretendRpcSend() {
-    pretendRpcReceiveWithTraceInfo(Trace.traceInfo());
+    pretendRpcReceiveWithTraceInfo(TraceInfo.fromSpan(Trace.currentSpan()));
   }
 
   public void pretendRpcReceiveWithTraceInfo(TraceInfo traceInfo) {
-    Span s = Trace.startSpan("received RPC", traceInfo);
+    TraceScope s = Trace.startSpan("received RPC", traceInfo);
     try {
       importantWork1();
     } finally {
-      s.stop();
+      s.close();
     }
   }
 }
\ No newline at end of file