diff --git a/java/.gitignore b/java/.gitignore
index d11829c..dba179d 100644
--- a/java/.gitignore
+++ b/java/.gitignore
@@ -2,3 +2,5 @@
.project
.settings/
target/
+.idea
+*.iml
diff --git a/java/pom.xml b/java/pom.xml
index 6b6822f..4f72a66 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -46,5 +46,11 @@
logback-classic
${logback.version}
+
+
+ io.opentracing.contrib
+ opentracing-okhttp3
+ 0.1.1-SNAPSHOT
+
diff --git a/java/src/main/java/lesson05/README.md b/java/src/main/java/lesson05/README.md
new file mode 100644
index 0000000..7863629
--- /dev/null
+++ b/java/src/main/java/lesson05/README.md
@@ -0,0 +1,23 @@
+# Lesson 5 - Using Existing Open Source Instrumentation
+
+## Objectives
+
+* Using Okhttp Open Source Instrumentation
+
+
+### Walkthrough
+
+
+### okhttp
+
+Adding manual instrumentation to okhttp like we did in [Lesson 3](../lesson03)
+is tedious. Fortunately, we don't need to do that because that instrumentation itself already exists
+as open source modules:
+
+ * https://github.com/opentracing-contrib/java-okhttp
+
+For an extra credit, try to use these modules to avoid instrumenting your code manually.
+
+
+
+
diff --git a/java/src/main/java/lesson05/solution/Formatter.java b/java/src/main/java/lesson05/solution/Formatter.java
new file mode 100644
index 0000000..c79badb
--- /dev/null
+++ b/java/src/main/java/lesson05/solution/Formatter.java
@@ -0,0 +1,58 @@
+package lesson05.solution;
+
+import com.google.common.collect.ImmutableMap;
+import io.dropwizard.Application;
+import io.dropwizard.Configuration;
+import io.dropwizard.setup.Environment;
+import io.jaegertracing.internal.JaegerTracer;
+import io.opentracing.Scope;
+import io.opentracing.Tracer;
+import lib.Tracing;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+public class Formatter extends Application {
+
+ private final Tracer tracer;
+
+ private Formatter(Tracer tracer) {
+ this.tracer = tracer;
+ }
+
+ @Path("/format")
+ @Produces(MediaType.TEXT_PLAIN)
+ public class FormatterResource {
+
+ @GET
+ public String format(@QueryParam("helloTo") String helloTo, @Context HttpHeaders httpHeaders) {
+ try (Scope scope = Tracing.startServerSpan(tracer, httpHeaders, "format")) {
+ String greeting = scope.span().getBaggageItem("greeting");
+ if (greeting == null) {
+ greeting = "Hello";
+ }
+ String helloStr = String.format("%s, %s!", greeting, helloTo);
+ scope.span().log(ImmutableMap.of("event", "string-format", "value", helloStr));
+ return helloStr;
+ }
+ }
+ }
+
+ @Override
+ public void run(Configuration configuration, Environment environment) throws Exception {
+ environment.jersey().register(new FormatterResource());
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.setProperty("dw.server.applicationConnectors[0].port", "8081");
+ System.setProperty("dw.server.adminConnectors[0].port", "9081");
+ try (JaegerTracer tracer = Tracing.init("formatter")) {
+ new Formatter(tracer).run(args);
+ }
+ }
+}
diff --git a/java/src/main/java/lesson05/solution/Hello.java b/java/src/main/java/lesson05/solution/Hello.java
new file mode 100644
index 0000000..29513a7
--- /dev/null
+++ b/java/src/main/java/lesson05/solution/Hello.java
@@ -0,0 +1,79 @@
+package lesson05.solution;
+
+import com.google.common.collect.ImmutableMap;
+import io.jaegertracing.internal.JaegerTracer;
+import io.opentracing.Scope;
+import io.opentracing.Tracer;
+import io.opentracing.contrib.okhttp3.TracingCallFactory;
+import lib.Tracing;
+import okhttp3.Call;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import java.io.IOException;
+
+public class Hello {
+
+ private final Tracer tracer;
+ private final Call.Factory traceClient;
+
+ private Hello(Tracer tracer) {
+ this.tracer = tracer;
+ traceClient = new TracingCallFactory(new OkHttpClient(), tracer);
+ }
+
+ private String getHttp(int port, String path, String param, String value) {
+ try {
+ HttpUrl url = new HttpUrl.Builder().scheme("http").host("localhost").port(port).addPathSegment(path)
+ .addQueryParameter(param, value).build();
+ Request.Builder requestBuilder = new Request.Builder().url(url);
+ Request request = requestBuilder.build();
+ Response response = traceClient.newCall(request).execute();
+ tracer.activeSpan().setTag("invoked", "okhttptracer");
+ if (response.code() != 200) {
+ throw new RuntimeException("Bad HTTP result: " + response);
+ }
+ return response.body().string();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void sayHello(String helloTo, String greeting) {
+ try (Scope scope = tracer.buildSpan("say-hello").startActive(true)) {
+ scope.span().setTag("hello-to", helloTo);
+ scope.span().setBaggageItem("greeting", greeting);
+
+ String helloStr = formatString(helloTo);
+ printHello(helloStr);
+ }
+ }
+
+ private String formatString(String helloTo) {
+ try (Scope scope = tracer.buildSpan("formatString").startActive(true)) {
+ String helloStr = getHttp(8081, "format", "helloTo", helloTo);
+ scope.span().log(ImmutableMap.of("event", "string-format", "value", helloStr));
+ return helloStr;
+ }
+ }
+
+ private void printHello(String helloStr) {
+ try (Scope scope = tracer.buildSpan("printHello").startActive(true)) {
+ getHttp(8082, "publish", "helloStr", helloStr);
+ scope.span().log(ImmutableMap.of("event", "println"));
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("Expecting two arguments, helloTo and greeting");
+ }
+ String helloTo = args[0];
+ String greeting = args[1];
+ try (JaegerTracer tracer = Tracing.init("hello-world")) {
+ new Hello(tracer).sayHello(helloTo, greeting);
+ }
+ }
+}
diff --git a/java/src/main/java/lesson05/solution/Publisher.java b/java/src/main/java/lesson05/solution/Publisher.java
new file mode 100644
index 0000000..5ff56f8
--- /dev/null
+++ b/java/src/main/java/lesson05/solution/Publisher.java
@@ -0,0 +1,54 @@
+package lesson05.solution;
+
+import com.google.common.collect.ImmutableMap;
+import io.dropwizard.Application;
+import io.dropwizard.Configuration;
+import io.dropwizard.setup.Environment;
+import io.jaegertracing.internal.JaegerTracer;
+import io.opentracing.Scope;
+import io.opentracing.Tracer;
+import lib.Tracing;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+public class Publisher extends Application {
+
+ private final Tracer tracer;
+
+ private Publisher(Tracer tracer) {
+ this.tracer = tracer;
+ }
+
+ @Path("/publish")
+ @Produces(MediaType.TEXT_PLAIN)
+ public class PublisherResource {
+
+ @GET
+ public String format(@QueryParam("helloStr") String helloStr, @Context HttpHeaders httpHeaders) {
+ try (Scope scope = Tracing.startServerSpan(tracer, httpHeaders, "publish")) {
+ System.out.println(helloStr);
+ scope.span().log(ImmutableMap.of("event", "println", "value", helloStr));
+ return "published";
+ }
+ }
+ }
+
+ @Override
+ public void run(Configuration configuration, Environment environment) throws Exception {
+ environment.jersey().register(new PublisherResource());
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.setProperty("dw.server.applicationConnectors[0].port", "8082");
+ System.setProperty("dw.server.adminConnectors[0].port", "9082");
+ try (JaegerTracer tracer = Tracing.init("publisher")) {
+ new Publisher(tracer).run(args);
+ }
+ }
+}