Merge pull request #1 from jgallimore/zipkin-v2

GERONIMO-6727 Zipkin v2 changes
diff --git a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/server/GeronimoOpenTracingFeature.java b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/server/GeronimoOpenTracingFeature.java
index 73c7f28..dbf197a 100644
--- a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/server/GeronimoOpenTracingFeature.java
+++ b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/server/GeronimoOpenTracingFeature.java
@@ -88,6 +88,7 @@
         final String path = Stream.of(
                 ofNullable(resourceInfo.getResourceClass().getAnnotation(Path.class)).map(Path::value).orElse(""),
                 ofNullable(resourceInfo.getResourceMethod().getAnnotation(Path.class)).map(Path::value).orElse(""))
+                .map(it -> it.equals("/") ? "" : it)
                 .map(it -> it.substring(it.startsWith("/") ? 1 : 0, it.endsWith("/") ? it.length() - 1 : it.length()))
                 .filter(it -> !it.isEmpty())
                 .collect(joining("/", "/", ""));
diff --git a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinConverter.java b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinConverter.java
index 195cff8..4238844 100644
--- a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinConverter.java
+++ b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinConverter.java
@@ -50,6 +50,7 @@
     private IdGenerator idGenerator;
 
     private String serviceName;
+    private boolean useV2 = false;
 
     public void setZipkinSpanEvent(final Bus<ZipkinSpan> zipkinSpanEvent) {
         this.zipkinSpanEvent = zipkinSpanEvent;
@@ -65,6 +66,7 @@
 
     public void init() {
         serviceName = config.read("zipkin.serviceName", getHostName() + "_" + getPid());
+        useV2 = Boolean.parseBoolean(config.read("span.converter.zipkin.http.useV2", "false").trim());
     }
 
     @Override
@@ -91,7 +93,14 @@
     private ZipkinSpan toZipkin(final SpanImpl span) {
         final ZipkinSpan.ZipkinEndpoint endpoint = toEndpoint(span);
 
-        final ZipkinSpan zipkin = new ZipkinSpan();
+        final ZipkinSpan zipkin;
+        if (useV2) {
+            zipkin = new ZipkinSpan();
+        } else {
+            zipkin = new ZipkinV1Span();
+            ((ZipkinV1Span) zipkin).setBinaryAnnotations(toBinaryAnnotations(span.getTags()));
+        }
+
         if (idGenerator.isCounter()) {
             zipkin.setParentId(asLong(span.getParentId()));
             zipkin.setTraceId(asLong(span.getTraceId()));
@@ -106,7 +115,7 @@
         zipkin.setTimestamp(span.getTimestamp());
         zipkin.setDuration(span.getDuration());
         zipkin.setAnnotations(toAnnotations(span));
-        zipkin.setBinaryAnnotations(toBinaryAnnotations(span.getTags()));
+
         zipkin.setTags(span.getTags().entrySet().stream().filter(e -> !Tags.SPAN_KIND.getKey().equalsIgnoreCase(e.getKey()))
                 .collect(toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue()))));
 
diff --git a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinHttp.java b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinHttp.java
index 2301e68..ca43c3c 100644
--- a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinHttp.java
+++ b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinHttp.java
@@ -22,6 +22,7 @@
 import static java.util.concurrent.TimeUnit.MINUTES;
 import static javax.ws.rs.client.Entity.entity;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static javax.ws.rs.core.MediaType.MEDIA_TYPE_WILDCARD;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -38,6 +39,7 @@
 import javax.json.bind.JsonbBuilder;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
 import org.apache.geronimo.microprofile.opentracing.common.config.GeronimoOpenTracingConfig;
@@ -178,7 +180,7 @@
 
     private void doSend(final List<ZipkinSpan> copy) {
         final Response result = client.target(collector)
-                .request(APPLICATION_JSON_TYPE)
+                .request()
                 .post(entity(copy, APPLICATION_JSON_TYPE));
         if (result.getStatus() >= 300) {
             // todo: better handling but at least log them to not loose them completely or explode in memory
diff --git a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinSpan.java b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinSpan.java
index ba1a555..5a9ae63 100644
--- a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinSpan.java
+++ b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinSpan.java
@@ -30,7 +30,6 @@
     private ZipkinEndpoint localEndpoint;
     private ZipkinEndpoint remoteEndpoint;
     private List<ZipkinAnnotation> annotations;
-    private List<ZipkinBinaryAnnotation> binaryAnnotations;
     private Map<String, String> tags;
     private Boolean debug;
     private Boolean shared;
@@ -91,14 +90,6 @@
         this.annotations = annotations;
     }
 
-    public List<ZipkinBinaryAnnotation> getBinaryAnnotations() {
-        return binaryAnnotations;
-    }
-
-    public void setBinaryAnnotations(final List<ZipkinBinaryAnnotation> binaryAnnotations) {
-        this.binaryAnnotations = binaryAnnotations;
-    }
-
     public Boolean getDebug() {
         return debug;
     }
diff --git a/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinV1Span.java b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinV1Span.java
new file mode 100644
index 0000000..f89d00f
--- /dev/null
+++ b/geronimo-opentracing-common/src/main/java/org/apache/geronimo/microprofile/opentracing/common/microprofile/zipkin/ZipkinV1Span.java
@@ -0,0 +1,31 @@
+/*
+ * 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.geronimo.microprofile.opentracing.common.microprofile.zipkin;
+
+import java.util.List;
+
+public class ZipkinV1Span extends ZipkinSpan {
+    private List<ZipkinBinaryAnnotation> binaryAnnotations;
+
+    public List<ZipkinBinaryAnnotation> getBinaryAnnotations() {
+        return binaryAnnotations;
+    }
+
+    public void setBinaryAnnotations(final List<ZipkinBinaryAnnotation> binaryAnnotations) {
+        this.binaryAnnotations = binaryAnnotations;
+    }
+}
diff --git a/geronimo-opentracing/pom.xml b/geronimo-opentracing/pom.xml
index 71f5f41..15dbac0 100644
--- a/geronimo-opentracing/pom.xml
+++ b/geronimo-opentracing/pom.xml
@@ -122,6 +122,12 @@
       <version>1.2</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>io.zipkin.zipkin2</groupId>
+      <artifactId>zipkin-junit</artifactId>
+      <version>2.12.9</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/BasicZipkinTest.java b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/BasicZipkinTest.java
new file mode 100644
index 0000000..a6b909d
--- /dev/null
+++ b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/BasicZipkinTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.opentracing.tck.setup;
+
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.arquillian.testng.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+import zipkin2.Span;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+import java.net.URL;
+import java.util.List;
+
+public class BasicZipkinTest extends Arquillian {
+
+    @Deployment
+    public static WebArchive createDeployment() {
+        return ShrinkWrap.create(WebArchive.class)
+                .addClasses(SimpleService.class)
+                .addAsWebInfResource(new ClassLoaderAsset("test-beans.xml"), "beans.xml")
+                .addAsServiceProvider(javax.enterprise.inject.spi.Extension.class, UseGeronimoTracerExtension.class);
+    }
+
+    private ZipkinRule zipkin;
+
+    @ArquillianResource
+    private URL serviceUrl;
+
+    @BeforeMethod
+    public void configure() {
+
+
+    }
+
+    @BeforeSuite
+    public void setup() {
+        zipkin = new ZipkinRule();
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.sender", "http");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.collector", zipkin.httpUrl() + "/api/v2/spans");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.bulkSendInterval", "6000");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.maxSpansPerBulk", "1");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.maxSpansIteration","1");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.bufferSize","1");
+        System.setProperty("geronimo.opentracing.span.converter.zipkin.http.useV2","true");
+
+    }
+
+
+    /**
+     * Test that server endpoint is adding standard tags
+     */
+    @Test
+    @RunAsClient
+    public void testSimpleService() throws Exception {
+        System.out.println(zipkin.httpUrl());
+
+        Client client = ClientBuilder.newClient();
+        String url = serviceUrl.toExternalForm() + "hello";
+
+        WebTarget target = client.target(url);
+        Response response = target.request().get();
+        if (response.getStatus() != 200) {
+            String unexpectedResponse = response.readEntity(String.class);
+            Assert.fail("Expected HTTP response code 200 but received " + response.getStatus() + "; Response: " + unexpectedResponse);
+        }
+
+        Thread.sleep(10000);
+
+        final List<List<Span>> traces = zipkin.getTraces();
+        Assert.assertTrue(traces.size() > 0, "Expected some traces");
+    }
+
+
+}
diff --git a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SimpleService.java b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SimpleService.java
new file mode 100644
index 0000000..fc2fdfd
--- /dev/null
+++ b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SimpleService.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.opentracing.tck.setup;
+
+import org.eclipse.microprofile.opentracing.Traced;
+
+import javax.enterprise.context.RequestScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+@Traced
+@RequestScoped
+@Path("hello")
+public class SimpleService {
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String sayHello() throws Exception {
+        return "Hello, world!";
+    }
+
+
+}
diff --git a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SkipOpentracingApiSetup.java b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SkipOpentracingApiSetup.java
index 788d6a4..01cfef1 100644
--- a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SkipOpentracingApiSetup.java
+++ b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/SkipOpentracingApiSetup.java
@@ -21,6 +21,9 @@
 import org.jboss.arquillian.core.spi.LoadableExtension;
 import org.jboss.shrinkwrap.api.Archive;
 import org.jboss.shrinkwrap.api.Filters;
+import org.jboss.shrinkwrap.api.Node;
+
+import java.util.Collection;
 
 // the tck put the opentracing-api in the war but not our impl, let's fix it by using the apploader api jar!
 public class SkipOpentracingApiSetup implements LoadableExtension {
@@ -34,8 +37,10 @@
 
         public void clean(@Observes final BeforeDeploy beforeDeploy) {
             final Archive<?> archive = beforeDeploy.getDeployment().getArchive();
-            archive.delete(archive.getContent(Filters.include("\\/WEB-INF\\/lib\\/opentracing\\-api\\-.*\\.jar")).values()
-                    .iterator().next().getPath());
+            final Collection<Node> opentracingApi = archive.getContent(Filters.include("\\/WEB-INF\\/lib\\/opentracing\\-api\\-.*\\.jar")).values();
+            if (opentracingApi != null && !opentracingApi.isEmpty()) {
+                archive.delete(opentracingApi.iterator().next().getPath());
+            }
         }
     }
 }
diff --git a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/UseGeronimoTracerExtension.java b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/UseGeronimoTracerExtension.java
new file mode 100644
index 0000000..1836018
--- /dev/null
+++ b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/UseGeronimoTracerExtension.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geronimo.microprofile.opentracing.tck.setup;
+
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+
+public class UseGeronimoTracerExtension implements Extension {
+
+    void vetoTckTracerIfScanned(@Observes final ProcessAnnotatedType<TckTracer> tckTracer) {
+        tckTracer.veto();
+    }
+
+}
diff --git a/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/ZipkinRule.java b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/ZipkinRule.java
new file mode 100644
index 0000000..08cda86
--- /dev/null
+++ b/geronimo-opentracing/src/test/java/org/apache/geronimo/microprofile/opentracing/tck/setup/ZipkinRule.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2015-2018 The OpenZipkin Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geronimo.microprofile.opentracing.tck.setup;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import okhttp3.HttpUrl;
+import okhttp3.mockwebserver.Dispatcher;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import okio.Buffer;
+import okio.GzipSource;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import zipkin2.Callback;
+import zipkin2.DependencyLink;
+import zipkin2.Span;
+import zipkin2.codec.SpanBytesDecoder;
+import zipkin2.collector.Collector;
+import zipkin2.collector.CollectorMetrics;
+import zipkin2.collector.InMemoryCollectorMetrics;
+import zipkin2.internal.Nullable;
+import zipkin2.internal.Platform;
+import zipkin2.junit.HttpFailure;
+import zipkin2.storage.InMemoryStorage;
+import zipkin2.storage.StorageComponent;
+
+import static okhttp3.mockwebserver.SocketPolicy.KEEP_OPEN;
+
+/**
+ * Starts up a local Zipkin server, listening for http requests on {@link #httpUrl}.
+ *
+ * <p>This can be used to test instrumentation. For example, you can POST spans directly to this
+ * server.
+ *
+ * <p>See http://openzipkin.github.io/zipkin-api/#/
+ */
+public final class ZipkinRule implements TestRule {
+    private final InMemoryStorage storage = InMemoryStorage.newBuilder().build();
+    private final InMemoryCollectorMetrics metrics = new InMemoryCollectorMetrics();
+    private final MockWebServer server = new MockWebServer();
+    private final BlockingQueue<MockResponse> failureQueue = new LinkedBlockingQueue<>();
+    private final AtomicInteger receivedSpanBytes = new AtomicInteger();
+
+    public ZipkinRule() {
+        Dispatcher dispatcher =
+                new Dispatcher() {
+                    final ZipkinDispatcher successDispatch = new ZipkinDispatcher(storage, metrics, server);
+
+                    @Override
+                    public MockResponse dispatch(RecordedRequest request) {
+                        MockResponse maybeFailure = failureQueue.poll();
+                        if (maybeFailure != null) return maybeFailure;
+                        MockResponse result = successDispatch.dispatch(request);
+                        if (request.getMethod().equals("POST")) {
+                            receivedSpanBytes.addAndGet((int) request.getBodySize());
+                        }
+                        return result;
+                    }
+
+                    @Override
+                    public MockResponse peek() {
+                        MockResponse maybeFailure = failureQueue.peek();
+                        if (maybeFailure != null) return maybeFailure.clone();
+                        return new MockResponse().setSocketPolicy(KEEP_OPEN);
+                    }
+                };
+        server.setDispatcher(dispatcher);
+    }
+
+    /** Use this to connect. The zipkin v1 interface will be under "/api/v1" */
+    public String httpUrl() {
+        return String.format("http://%s:%s", server.getHostName(), server.getPort());
+    }
+
+    /** Use this to see how many requests you've sent to any zipkin http endpoint. */
+    public int httpRequestCount() {
+        return server.getRequestCount();
+    }
+
+    /** Use this to see how many spans or serialized bytes were collected on the http endpoint. */
+    public InMemoryCollectorMetrics collectorMetrics() {
+        return metrics;
+    }
+
+    /** Retrieves all traces this zipkin server has received. */
+    public List<List<Span>> getTraces() {
+        return storage.spanStore().getTraces();
+    }
+
+    /** Retrieves a trace by ID which zipkin server has received, or null if not present. */
+    @Nullable
+    public List<Span> getTrace(String traceId) {
+        try {
+            return storage.spanStore().getTrace(traceId).execute();
+        } catch (IOException e) {
+            throw Platform.get().assertionError("I/O exception in in-memory storage", e);
+        }
+    }
+
+    /** Retrieves all service links between traces this zipkin server has received. */
+    public List<DependencyLink> getDependencies() {
+        return storage.spanStore().getDependencies();
+    }
+
+    /**
+     * Used to manually start the server.
+     *
+     * @param httpPort choose 0 to select an available port
+     */
+    public void start(int httpPort) throws IOException {
+        server.start(httpPort);
+    }
+
+    /** Used to manually stop the server. */
+    public void shutdown() throws IOException {
+        server.shutdown();
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return server.apply(base, description);
+    }
+
+    final class ZipkinDispatcher extends Dispatcher {
+        private final Collector consumer;
+        private final CollectorMetrics metrics;
+        private final MockWebServer server;
+
+        ZipkinDispatcher(StorageComponent storage, CollectorMetrics metrics, MockWebServer server) {
+            this.consumer = Collector.newBuilder(getClass()).storage(storage).metrics(metrics).build();
+            this.metrics = metrics;
+            this.server = server;
+        }
+
+        @Override
+        public MockResponse dispatch(RecordedRequest request) {
+            HttpUrl url = server.url(request.getPath());
+            if (request.getMethod().equals("POST")) {
+                String type = request.getHeader("Content-Type");
+                if (url.encodedPath().equals("/api/v1/spans")) {
+                    SpanBytesDecoder decoder =
+                            type != null && type.contains("/x-thrift")
+                                    ? SpanBytesDecoder.THRIFT
+                                    : SpanBytesDecoder.JSON_V1;
+
+                    return acceptSpans(request, decoder);
+                } else if (url.encodedPath().equals("/api/v2/spans")) {
+                    SpanBytesDecoder decoder =
+                            type != null && type.contains("/x-protobuf")
+                                    ? SpanBytesDecoder.PROTO3
+                                    : SpanBytesDecoder.JSON_V2;
+
+                    return acceptSpans(request, decoder);
+                }
+            } else { // unsupported method
+                return new MockResponse().setResponseCode(405);
+            }
+            return new MockResponse().setResponseCode(404);
+        }
+
+        MockResponse acceptSpans(RecordedRequest request, SpanBytesDecoder decoder) {
+            metrics.incrementMessages();
+            byte[] body = request.getBody().readByteArray();
+
+            // check for the presence of binaryAnnotations, which should not be present in V2.
+            // @see ZipkinHttpCollector.testForUnexpectedFormat
+            if (SpanBytesDecoder.JSON_V2.equals(decoder) &&
+                    new String(body).contains("\"binaryAnnotations\"")) {
+                final MockResponse mockResponse = new MockResponse();
+                mockResponse.setResponseCode(400);
+                mockResponse.setBody("Expected a JSON_V2 encoded list, but received: JSON_V1\n");
+                return mockResponse;
+            }
+
+            String encoding = request.getHeader("Content-Encoding");
+            if (encoding != null && encoding.contains("gzip")) {
+                try {
+                    Buffer result = new Buffer();
+                    GzipSource source = new GzipSource(new Buffer().write(body));
+                    while (source.read(result, Integer.MAX_VALUE) != -1) ;
+                    body = result.readByteArray();
+                } catch (IOException e) {
+                    metrics.incrementMessagesDropped();
+                    return new MockResponse().setResponseCode(400).setBody("Cannot gunzip spans");
+                }
+            }
+
+            final MockResponse result = new MockResponse();
+            consumer.acceptSpans(
+                    body,
+                    decoder,
+                    new Callback<Void>() {
+                        @Override
+                        public void onSuccess(Void value) {
+                            result.setResponseCode(202);
+                        }
+
+                        @Override
+                        public void onError(Throwable t) {
+                            String message = t.getMessage();
+                            result.setBody(message).setResponseCode(message.startsWith("Cannot store") ? 500 : 400);
+                        }
+                    });
+            return result;
+        }
+    }
+
+}
diff --git a/geronimo-opentracing/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/geronimo-opentracing/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
index c34b326..5339e85 100644
--- a/geronimo-opentracing/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
+++ b/geronimo-opentracing/src/test/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -1 +1 @@
-org.apache.geronimo.microprofile.opentracing.tck.setup.SkipOpentracingApiSetup
+org.apache.geronimo.microprofile.opentracing.tck.setup.SkipOpentracingApiSetup
\ No newline at end of file
diff --git a/geronimo-opentracing/src/test/resources/test-beans.xml b/geronimo-opentracing/src/test/resources/test-beans.xml
new file mode 100644
index 0000000..f2297ba
--- /dev/null
+++ b/geronimo-opentracing/src/test/resources/test-beans.xml
@@ -0,0 +1,26 @@
+<?xml version="1.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.
+-->
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+        http://xmlns.jcp.org/xml/ns/javaee
+        http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
+       bean-discovery-mode="annotated"
+       version="2.0">
+    <trim/>
+</beans>