From 2575fb2e226d2d7efc47a3919bd9d575a8230f75 Mon Sep 17 00:00:00 2001 From: Jonathan Mace Date: Wed, 10 May 2017 17:13:57 -0400 Subject: [PATCH] Updating the static APIs in transit layer --- .../main/java/brown/tracingplane/Baggage.java | 16 +- .../tracingplane/DefaultBaggageProvider.java | 1 + .../java/brown/tracingplane/TransitLayer.java | 136 ++++++++++++++ transitlayer/staticapi/pom.xml | 5 + .../brown/tracingplane/ActiveBaggage.java | 168 ++++++++++++++++++ .../tracingplane/DefaultTransitLayer.java | 66 +++++++ .../tracingplane/ThreadLocalTransitLayer.java | 95 ++++++++++ 7 files changed, 479 insertions(+), 8 deletions(-) create mode 100644 transitlayer/api/src/main/java/brown/tracingplane/TransitLayer.java create mode 100644 transitlayer/staticapi/src/main/java/brown/tracingplane/ActiveBaggage.java create mode 100644 transitlayer/staticapi/src/main/java/brown/tracingplane/DefaultTransitLayer.java create mode 100644 transitlayer/tls-impl/src/main/java/brown/tracingplane/ThreadLocalTransitLayer.java diff --git a/baggagecontext/staticapi/src/main/java/brown/tracingplane/Baggage.java b/baggagecontext/staticapi/src/main/java/brown/tracingplane/Baggage.java index 1e51fd9..89e7bd9 100644 --- a/baggagecontext/staticapi/src/main/java/brown/tracingplane/Baggage.java +++ b/baggagecontext/staticapi/src/main/java/brown/tracingplane/Baggage.java @@ -25,7 +25,7 @@ public class Baggage { /** * @return a new instance of {@link B}, which may be null to indicate an empty baggage */ - public BaggageContext newInstance() { + public static BaggageContext newInstance() { return provider.newInstance(); } @@ -36,7 +36,7 @@ public class Baggage { * * @param baggage a baggage context */ - public void discard(BaggageContext baggage) { + public static void discard(BaggageContext baggage) { provider.discard(baggage); } @@ -48,7 +48,7 @@ public class Baggage { * @param from a baggage instance, possibly null * @return a baggage instance branched from from, possibly null */ - public BaggageContext branch(BaggageContext from) { + public static BaggageContext branch(BaggageContext from) { return provider.branch(from); } @@ -60,7 +60,7 @@ public class Baggage { * @param right a baggage instance, possibly null * @return a baggage instance with merged contents from left and right */ - public BaggageContext join(BaggageContext left, BaggageContext right) { + public static BaggageContext join(BaggageContext left, BaggageContext right) { return provider.join(left, right); } @@ -72,7 +72,7 @@ public class Baggage { * @param length lenft of the baggage bytes * @return a deserialized baggage instance, possibly null */ - public BaggageContext deserialize(byte[] serialized, int offset, int length) { + public static BaggageContext deserialize(byte[] serialized, int offset, int length) { return provider.deserialize(serialized, offset, length); } @@ -82,7 +82,7 @@ public class Baggage { * @param buf a serialized baggage * @return a deserialized baggage instance, possibly null */ - public BaggageContext deserialize(ByteBuffer buf) { + public static BaggageContext deserialize(ByteBuffer buf) { return provider.deserialize(buf); } @@ -93,7 +93,7 @@ public class Baggage { * @param baggage a baggage instance to serialize, possibly null * @return the serialized representation of baggage, which might be null or an empty byte array */ - public byte[] serialize(BaggageContext baggage) { + public static byte[] serialize(BaggageContext baggage) { return provider.serialize(baggage); } @@ -106,7 +106,7 @@ public class Baggage { * @param maximumSerializedSize the maximum size in bytes of the serialized baggage. * @return the serialized representation of baggage, which might be null or an empty byte array */ - public byte[] serialize(BaggageContext baggage, int maximumSerializedSize) { + public static byte[] serialize(BaggageContext baggage, int maximumSerializedSize) { return provider.serialize(baggage, maximumSerializedSize); } diff --git a/baggagecontext/staticapi/src/main/java/brown/tracingplane/DefaultBaggageProvider.java b/baggagecontext/staticapi/src/main/java/brown/tracingplane/DefaultBaggageProvider.java index 69ce351..01134da 100644 --- a/baggagecontext/staticapi/src/main/java/brown/tracingplane/DefaultBaggageProvider.java +++ b/baggagecontext/staticapi/src/main/java/brown/tracingplane/DefaultBaggageProvider.java @@ -44,6 +44,7 @@ public class DefaultBaggageProvider { if (instance == null) { instance = new NoOpBaggageContextProvider(); } + log.info("Baggage provider initialied to " + instance.getClass().getName()); } } diff --git a/transitlayer/api/src/main/java/brown/tracingplane/TransitLayer.java b/transitlayer/api/src/main/java/brown/tracingplane/TransitLayer.java new file mode 100644 index 0000000..f71b8d8 --- /dev/null +++ b/transitlayer/api/src/main/java/brown/tracingplane/TransitLayer.java @@ -0,0 +1,136 @@ +package brown.tracingplane; + +import java.nio.ByteBuffer; + +/** + *

+ * A {@link TransitLayer} implementation is responsible for maintaining {@link BaggageContext} instances while + * executions run. The TransitLayer has the notion of an "active" context which represents the {@link BaggageContext} + * for the current execution. Implicitly, if the current execution has no {@link BaggageContext}, it is the same as + * having an empty context. + *

+ * + *

+ * A {@link TransitLayer} implementation typically maintains the active {@link BaggageContext} using thread-local + * storage. The purpose of this interface is to enable other implementations to plug in to existing instrumentation. + *

+ * + *

+ * {@link TransitLayer} also provides methods that mirror those of {@link BaggageProvider}, such as {@link #branch()} + * which corresponds to {@link BaggageProvider#branch(BaggageContext)}. These methods simply proxy the + * {@link BaggageProvider} method, passing the currently active {@link BaggageContext}. + *

+ * + *

+ * All {@link TransitLayer} implementations must have a one-argument constructor that receives the + * {@link BaggageProvider} to use. + *

+ */ +public interface TransitLayer { + + /** + * Discard the currently active {@link BaggageContext}. + */ + public void discard(); + + /** + * Create and return a branched copy of the currently active baggage context. Typically, this will just duplicate + * the active context or increment a reference count. Sometimes it will create a new instance, or even modify the + * contents of the branched context. + * + * @return a baggage instance branched from the currently active context, possibly null + */ + public BaggageContext branch(); + + /** + * Create and return a branched, serialized copy of the currently active baggage context. Typically, this will just + * duplicate the active context or increment a reference count. Sometimes it will create a new instance, or even + * modify the contents of the branched context. + * + * @return a baggage instance branched from the currently active context and serialized, possibly null + */ + public byte[] branchBytes(); + + /** + * Merges the contents of otherContext into the currently active context. otherContext + * should not be reused after calling this method, and should be treated as discarded. + * + * @param otherContext another baggage context, possibly null + */ + public void join(BaggageContext otherContext); + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public void join(ByteBuffer serializedContext); + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public void join(byte[] serialized, int offset, int length); + + /** + * Discards the currently active {@link BaggageContext}, then activates the provided baggage. If + * baggage is just a modified version of the currently active BaggageContext, then it is better to use + * the {@link #update(BaggageContext)} method instead. + * + * @param baggage The new baggage context to activate. + */ + public void set(BaggageContext baggage); + + /** + * Deserializes the provided context, discards any currently active context, and replaces it with the deserialized + * context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public void set(ByteBuffer serializedContext); + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serialized a serialized baggage context, possibly null + * @param offset offset into the byte array + * @param length length of serialized bytes + */ + public void set(byte[] serialized, int offset, int length); + + /** + * Gets and removes the currently active {@link BaggageContext}. After calling this method, there will be no active + * {@link BaggageContext}. + * + * @return the current {@link BaggageContext}. + */ + public BaggageContext take(); + + /** + * Gets, removes, and serializes the currently active {@link BaggageContext}. After calling this method, there will + * be no active {@link BaggageContext}. + * + * @return the current {@link BaggageContext}. + */ + public byte[] takeBytes(); + + /** + * Gets the currently active {@link BaggageContext}. The {@link BaggageContext} instance remains active after + * calling this method. Use {@link #take()} to if you wish to get and remove the currently active context. + * + * @return the active {@link BaggageContext} + */ + public BaggageContext peek(); + + /** + * Sets the currently active {@link BaggageContext}. A call to this method implies that the provided + * context argument is an updated version of the active context. Conversely, if you intend to replace + * the currently active context (e.g., because a different execution is beginning), use the + * {@link #set(BaggageContext)} method. + * + * @param context an updated version of the currently active baggage context. + */ + public void update(BaggageContext baggage); + +} diff --git a/transitlayer/staticapi/pom.xml b/transitlayer/staticapi/pom.xml index 5b1ef2f..c3f2053 100644 --- a/transitlayer/staticapi/pom.xml +++ b/transitlayer/staticapi/pom.xml @@ -24,6 +24,11 @@ transitlayer-api ${project.version} + + brown.tracingplane + transitlayer-impl + ${project.version} + brown.tracingplane baggagecontext-staticapi diff --git a/transitlayer/staticapi/src/main/java/brown/tracingplane/ActiveBaggage.java b/transitlayer/staticapi/src/main/java/brown/tracingplane/ActiveBaggage.java new file mode 100644 index 0000000..68174f7 --- /dev/null +++ b/transitlayer/staticapi/src/main/java/brown/tracingplane/ActiveBaggage.java @@ -0,0 +1,168 @@ +package brown.tracingplane; + +import java.nio.ByteBuffer; + +/** + *

+ * {@link ActiveBaggage} provides static methods that mirror the methods implemented by {@link BaggageProvider} and + * {@link TransitLayer}. Unlike the {@link brown.tracingplane.Baggage} interface, {@link ActiveBaggage} implicitly + * accesses the currently-active {@link BaggageContext} that is being managed by the {@link TransitLayer}. Unless it has + * been configured otherwise, this entails looking up the {@link BaggageContext} in thread-local storage. + *

+ * + *

+ * This class also provides static methods to get and set the currently active baggage. These methods proxy to the + * configured {@link TransitLayer}, which is responsible for maintaining active baggage (e.g., in thread-local storage). + *

+ * + *

+ * If you wish to manipulate {@link BaggageContext} instances without affecting the currently active baggage context, + * use the static methods on the {@link brown.tracingplane.Baggage} class. + *

+ * + *

+ * Using this class requires that a {@link BaggageProvider} has been registered (e.g., using the + * baggage.provider property). By default, the {@link TransitLayer} used will be + * {@link ThreadLocalTransitLayer}; this can be overridden using baggage.transit. + *

+ * + */ +public class ActiveBaggage { + + private static final TransitLayer transit = DefaultTransitLayer.get(); + + /** Not instantiable */ + private ActiveBaggage() {} + + /** + * Discard the currently active {@link BaggageContext}. + */ + public static void discard() { + transit.discard(); + } + + /** + * Create and return a branched copy of the currently active baggage context. Typically, this will just duplicate + * the active context or increment a reference count. Sometimes it will create a new instance, or even modify the + * contents of the branched context. + * + * @return a baggage instance branched from the currently active context, possibly null + */ + public static BaggageContext branch() { + return transit.branch(); + } + + /** + * Create and return a branched, serialized copy of the currently active baggage context. Typically, this will just + * duplicate the active context or increment a reference count. Sometimes it will create a new instance, or even + * modify the contents of the branched context. + * + * @return a baggage instance branched from the currently active context and serialized, possibly null + */ + public static byte[] branchBytes() { + return transit.branchBytes(); + } + + /** + * Merges the contents of otherContext into the currently active context. otherContext + * should not be reused after calling this method, and should be treated as discarded. + * + * @param otherContext another baggage context, possibly null + */ + public static void join(BaggageContext otherContext) { + transit.join(otherContext); + } + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public static void join(ByteBuffer serializedContext) { + transit.join(serializedContext); + } + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public static void join(byte[] serialized, int offset, int length) { + transit.join(serialized, offset, length); + } + + /** + * Discards the currently active {@link BaggageContext}, then activates the provided baggage. If + * baggage is just a modified version of the currently active BaggageContext, then it is better to use + * the {@link #update(BaggageContext)} method instead. + * + * @param baggage The new baggage context to activate. + */ + public static void set(BaggageContext baggage) { + transit.set(baggage); + } + + /** + * Deserializes the provided context, discards any currently active context, and replaces it with the deserialized + * context. + * + * @param serializedContext a serialized baggage context, possibly null + */ + public static void set(ByteBuffer serializedContext) { + transit.set(serializedContext); + } + + /** + * Deserializes the provided context and merges it into the currently active context. + * + * @param serialized a serialized baggage context, possibly null + * @param offset offset into the byte array + * @param length length of serialized bytes + */ + public static void set(byte[] serialized, int offset, int length) { + transit.set(serialized, offset, length); + } + + /** + * Gets and removes the currently active {@link BaggageContext}. After calling this method, there will be no active + * {@link BaggageContext}. + * + * @return the current {@link BaggageContext}. + */ + public static BaggageContext take() { + return transit.take(); + } + + /** + * Gets, removes, and serializes the currently active {@link BaggageContext}. After calling this method, there will + * be no active {@link BaggageContext}. + * + * @return the current {@link BaggageContext}. + */ + public static byte[] takeBytes() { + return transit.takeBytes(); + } + + /** + * Gets the currently active {@link BaggageContext}. The {@link BaggageContext} instance remains active after + * calling this method. Use {@link #take()} to if you wish to get and remove the currently active context. + * + * @return the active {@link BaggageContext} + */ + public static BaggageContext peek() { + return transit.peek(); + } + + /** + * Sets the currently active {@link BaggageContext}. A call to this method implies that the provided + * context argument is an updated version of the active context. Conversely, if you intend to replace + * the currently active context (e.g., because a different execution is beginning), use the + * {@link #set(BaggageContext)} method. + * + * @param context an updated version of the currently active baggage context. + */ + public static void update(BaggageContext baggage) { + transit.update(baggage); + } + +} diff --git a/transitlayer/staticapi/src/main/java/brown/tracingplane/DefaultTransitLayer.java b/transitlayer/staticapi/src/main/java/brown/tracingplane/DefaultTransitLayer.java new file mode 100644 index 0000000..8d603b9 --- /dev/null +++ b/transitlayer/staticapi/src/main/java/brown/tracingplane/DefaultTransitLayer.java @@ -0,0 +1,66 @@ +package brown.tracingplane; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.typesafe.config.ConfigException; +import com.typesafe.config.ConfigFactory; + +/** + *

+ * Loads the default configured {@link TransitLayer} using reflection. Checks the baggage.transit property. + * If baggage.transit is not set, then the default transit layer will be {@link ThreadLocalTransitLayer}. + *

+ */ +public class DefaultTransitLayer { + + private static final Logger log = LoggerFactory.getLogger(DefaultTransitLayer.class); + + /** Not instantiable */ + private DefaultTransitLayer() {} + + private static boolean initialized = false; + private static TransitLayer instance = null; + + private static synchronized void initialize() { + if (initialized) { + return; + } + BaggageProvider provider = DefaultBaggageProvider.getWrapped(); + try { + String providerClass = ConfigFactory.load().getString("baggage.transit"); + try { + Constructor constructor = Class.forName(providerClass).getDeclaredConstructor(BaggageProvider.class); + instance = (TransitLayer) constructor.newInstance(provider); + } catch (NoSuchMethodException | SecurityException | ClassNotFoundException | InstantiationException + | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + log.error("Unable to instantiate baggage.transit " + providerClass, e); + } + } catch (ConfigException.Missing e) { + log.error("No baggage.transit has been configured"); + } catch (ConfigException.WrongType e) { + log.error("Invalid value (expected a string) for baggage.transit " + + ConfigFactory.load().getAnyRef("baggage.transit")); + } finally { + initialized = true; + if (instance == null) { + instance = new ThreadLocalTransitLayer(provider); + } + log.info("Transit Layer initialied to " + instance.getClass().getName()); + } + } + + /** + * @return the configured {@link TransitLayer} instance. The default transit layer can be set using + * -Dbaggage.transit. If no instance has been configured, this method will return a + * {@link ThreadLocalTransitLayer} that uses simple thread-local storage to store baggage contexts. + */ + public static TransitLayer get() { + if (!initialized) { + initialize(); + } + return instance; + } + +} diff --git a/transitlayer/tls-impl/src/main/java/brown/tracingplane/ThreadLocalTransitLayer.java b/transitlayer/tls-impl/src/main/java/brown/tracingplane/ThreadLocalTransitLayer.java new file mode 100644 index 0000000..4ec0511 --- /dev/null +++ b/transitlayer/tls-impl/src/main/java/brown/tracingplane/ThreadLocalTransitLayer.java @@ -0,0 +1,95 @@ +package brown.tracingplane; + +import java.nio.ByteBuffer; + +/** + *

+ * A straightforward {@link TransitLayer} implementation based on thread-local storage. + *

+ */ +public class ThreadLocalTransitLayer implements TransitLayer { + + private final BaggageProvider provider; + private final ThreadLocal current = new ThreadLocal(); + + /** + * @param provider the implementation of {@link BaggageProvider} in use by this transit layer + */ + public ThreadLocalTransitLayer(BaggageProvider provider) { + this.provider = provider; + } + + @Override + public void discard() { + current.remove(); + } + + @Override + public BaggageContext branch() { + return provider.branch(current.get()); + } + + @Override + public byte[] branchBytes() { + return provider.serialize(provider.branch(current.get())); + } + + @Override + public void join(BaggageContext otherContext) { + current.set(provider.join(current.get(), otherContext)); + } + + @Override + public void join(ByteBuffer serializedContext) { + current.set(provider.join(current.get(), provider.deserialize(serializedContext))); + } + + @Override + public void join(byte[] serialized, int offset, int length) { + current.set(provider.join(current.get(), provider.deserialize(serialized, offset, length))); + } + + @Override + public void set(BaggageContext baggage) { + current.set(baggage); + } + + @Override + public void set(ByteBuffer serializedContext) { + current.set(provider.deserialize(serializedContext)); + } + + @Override + public void set(byte[] serialized, int offset, int length) { + current.set(provider.deserialize(serialized, offset, length)); + } + + @Override + public BaggageContext take() { + try { + return current.get(); + } finally { + current.remove(); + } + } + + @Override + public byte[] takeBytes() { + try { + return provider.serialize(current.get()); + } finally { + current.remove(); + } + } + + @Override + public BaggageContext peek() { + return current.get(); + } + + @Override + public void update(BaggageContext baggage) { + current.set(baggage); + } + +} -- GitLab