Commit be145be6 authored by Jonathan Mace's avatar Jonathan Mace

Merge baggage context noop-impl into staticapi, add some tests

parent 2575fb2e
# Tracing Plane - Core
Defines two core Tracing Plane interfaces - `BaggageContext` and `BaggageProvider`.
\ No newline at end of file
Defines two core Tracing Plane interfaces - `BaggageContext` and `BaggageProvider`.
A `BaggageContext` supports several fundamental propagation operations, that are implemented by its `BaggageProvider`. They are:
* `newInstance()` -- creates a new, empty `BaggageContext`. Depending on the `BaggageProvider`, it may choose to represent empty `BaggageContext` instances using null
* `branch(BaggageContext)` -- creates a duplicate `BaggageContext`. Typically this just creates a copy. Changes made to the branched `BaggageContext` will not be visible in the original, and vice versa.
* `join(BaggageContext, BaggageContext)` -- merges the values of two `BaggageContext` instances. If there is no conflicting data within the `BaggageContexts`, this resembles a union of their contents. However, if they both contain, e.g., values mapped to the same key, and those values differ, then the `BaggageProvider` must implement some sort of conflict resolution.
* `serialize(BaggageContext)` -- serializes the `BaggageContext` to a binary representation. For a string-based representation, either use a `BaggageProvider`-specified representation, or `base64` encode the binary representation
* `deserialize(BaggageContext)` -- corresponding deserialization method
* `trim` -- trim is a special operation, that is exposed as `serialize(BaggageContext, maximumSerializedLength)`. `BaggageProvider` is expected to provide a serialization method that drops data if it exceeds a certain length threshold.
The above methods only pertain to propagating `BaggageContext`. There are no methods for accessing `BaggageContext` data or values. The `BaggageProvider` is responsible for providing accessor interfaces.
With respect to the Tracing Plane, we provide a concise, efficient implementation of all of these methods using a representation based on *atoms*.
\ No newline at end of file
package brown.tracingplane;
/**
* It wouldn't be Java without some a camel-case caravan of nouns.
*
* {@link BaggageProviderFactory} is expected to have a no-arg constructor so that it can be instantiated by reflection.
*/
public interface BaggageProviderFactory {
/**
* Instantiate the {@link BaggageProvider} that this factory provides.
*
* @return a {@link BaggageProvider} instance
*/
public BaggageProvider<? extends BaggageContext> provider();
}
.classpath
.settings
.project
bin
target
xtrace-data
application.conf
\ No newline at end of file
# No-Op BaggageContext Impl
Provides a no-op implementation of the `BaggageContext` and `BaggageProvider` interfaces. Every propagation method just returns null.
Having a no-op baggage implementation is useful at instrumentation time, because you can instrument your systems and compile them without yet deciding on the actual `BaggageProvider` implementation you wish to use.
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>brown.tracingplane</groupId>
<artifactId>baggagecontext-noopimpl</artifactId>
<packaging>jar</packaging>
<name>Baggage Context - No-Op Impl</name>
<parent>
<groupId>brown.tracingplane</groupId>
<artifactId>baggagecontext</artifactId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>brown.tracingplane</groupId>
<artifactId>baggagecontext-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
</plugins>
</build>
</project>
......@@ -10,7 +10,6 @@
<modules>
<module>api</module>
<module>staticapi</module>
<module>noop-impl</module>
</modules>
<parent>
......
# Tracing Plane - Static API
The static `BaggageContext` API provides the following:
* Enables registration of a `BaggageProvider` class using the `baggage.provider` property
* Provides static methods in the `brown.tracingplane.Baggage` class that proxy to the configured `BaggageProvider`.
By default, `baggage.provider` will not be set; if this is the case, a No-Op provider will be used that simply ignores all baggage.
\ No newline at end of file
......@@ -19,11 +19,6 @@
<artifactId>baggagecontext-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>brown.tracingplane</groupId>
<artifactId>baggagecontext-noopimpl</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
......
......@@ -17,7 +17,7 @@ import java.nio.ByteBuffer;
*/
public class Baggage {
private static final BaggageProvider<BaggageContext> provider = DefaultBaggageProvider.getWrapped();
static BaggageProvider<BaggageContext> provider = DefaultBaggageProvider.getWrapped();
/** Not instantiable */
private Baggage() {}
......
......@@ -2,9 +2,11 @@ package brown.tracingplane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigFactory;
import brown.tracingplane.noopprovider.NoOpBaggageContextProvider;
import brown.tracingplane.impl.NoOpBaggageContextProvider;
import brown.tracingplane.impl.NoOpBaggageContextProviderFactory;
/**
* <p>
......@@ -16,36 +18,64 @@ public class DefaultBaggageProvider {
private static final Logger log = LoggerFactory.getLogger(DefaultBaggageProvider.class);
/** Not instantiable */
private DefaultBaggageProvider() {}
private static boolean initialized = false;
private static BaggageProvider<? extends BaggageContext> instance = null;
private final BaggageProviderFactory factory;
private final BaggageProvider<? extends BaggageContext> provider;
private final BaggageProvider<BaggageContext> wrappedProvider;
@SuppressWarnings("unchecked")
private static synchronized void initialize() {
if (initialized) {
return;
}
try {
String providerClass = ConfigFactory.load().getString("baggage.provider");
private DefaultBaggageProvider() {
Config config = ConfigFactory.load();
BaggageProviderFactory factory = null;
if (!config.hasPath("baggage.provider")) {
log.warn("No BaggageProviderFactory has been configured using baggage.provider -- baggage propagation will be disabled");
} else {
try {
instance = (BaggageProvider<? extends BaggageContext>) Class.forName(providerClass).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
log.error("Unable to instantiate baggage.provider " + providerClass, e);
String providerClass = config.getString("baggage.provider");
try {
Object instantiated = Class.forName(providerClass).newInstance();
try {
factory = (BaggageProviderFactory) instantiated;
} catch (ClassCastException e) {
log.error("The configured baggage.provider should be an instance of BaggageProviderFactory; found " +
instantiated.getClass().getName());
}
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
log.error("Unable to instantiate BaggageProviderFactory specified by baggage.provider " +
providerClass, e);
}
} catch (ConfigException.WrongType e) {
Object v = config.getAnyRef("baggage.provider");
log.error("Invalid baggage.provider has been configured -- baggage propagation will be disabled. Expected a string for baggage.provider, found " +
v.getClass().getName() + ": " + v);
}
} catch (ConfigException.Missing e) {
log.error("No baggage.provider has been configured");
} catch (ConfigException.WrongType e) {
log.error("Invalid value (expected a string) for baggage.provider " +
ConfigFactory.load().getAnyRef("baggage.provider"));
} finally {
initialized = true;
if (instance == null) {
instance = new NoOpBaggageContextProvider();
}
if (factory == null) {
this.factory = new NoOpBaggageContextProviderFactory();
this.wrappedProvider = new NoOpBaggageContextProvider();
this.provider = this.wrappedProvider;
} else {
this.factory = factory;
this.provider = factory.provider();
this.wrappedProvider = BaggageProviderProxy.wrap(this.provider);
}
}
private static DefaultBaggageProvider instance = null;
private static DefaultBaggageProvider instance() {
if (instance == null) {
synchronized (DefaultBaggageProvider.class) {
if (instance == null) {
instance = new DefaultBaggageProvider();
}
}
log.info("Baggage provider initialied to " + instance.getClass().getName());
}
return instance;
}
/**
......@@ -54,10 +84,7 @@ public class DefaultBaggageProvider {
* {@link NoOpBaggageContextProvider}.
*/
public static BaggageProvider<? extends BaggageContext> get() {
if (!initialized) {
initialize();
}
return instance;
return instance().provider;
}
/**
......@@ -66,12 +93,7 @@ public class DefaultBaggageProvider {
* will return a {@link NoOpBaggageContextProvider}.
*/
public static BaggageProvider<BaggageContext> getWrapped() {
BaggageProvider<? extends BaggageContext> provider = get();
if (provider instanceof NoOpBaggageContextProvider) {
return (NoOpBaggageContextProvider) provider;
} else {
return BaggageProviderProxy.wrap(provider);
}
return instance().wrappedProvider;
}
}
package brown.tracingplane.noopprovider;
package brown.tracingplane.impl;
import java.nio.ByteBuffer;
import brown.tracingplane.BaggageContext;
......
package brown.tracingplane.impl;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.BaggageProvider;
import brown.tracingplane.BaggageProviderFactory;
/**
* A {@link BaggageProvider} implementation that always just returns null.
*/
public class NoOpBaggageContextProviderFactory implements BaggageProviderFactory {
@Override
public BaggageProvider<? extends BaggageContext> provider() {
return new NoOpBaggageContextProvider();
}
}
package brown.tracingplane;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import brown.tracingplane.impl.NoOpBaggageContextProvider;
import brown.tracingplane.impl.NoOpBaggageContextProviderFactory;
public class TestNoOpBaggageContextProvider {
@Test
public void testNoOpBaggageContextProviderFactory() {
BaggageProviderFactory factory = new NoOpBaggageContextProviderFactory();
BaggageProvider<?> provider = factory.provider();
assertNotNull(provider);
assertTrue(provider instanceof NoOpBaggageContextProvider);
}
@Test
public void testNoOpBaggageContextProvider() {
BaggageProvider<?> provider = new NoOpBaggageContextProvider();
assertNull(provider.newInstance());
assertNull(provider.branch(null));
assertNull(provider.join(null, null));
assertNull(provider.serialize(null));
assertNull(provider.serialize(null, 0));
assertNull(provider.deserialize(null));
assertNull(provider.deserialize(null, 0, 0));
assertTrue(provider.isValid(null));
BaggageContext invalidContext = new BaggageContext() {};
assertFalse(provider.isValid(invalidContext));
}
}
package brown.tracingplane;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import org.junit.Test;
import brown.tracingplane.impl.NoOpBaggageContextProvider;
public class TestStaticAPI {
@Test
public void testDefaultBaggageProviderIsNoOp() {
BaggageProvider<?> provider = DefaultBaggageProvider.get();
assertNotNull(provider);
assertTrue(provider instanceof NoOpBaggageContextProvider);
}
@Test
public void testStaticAPICallsInvokeDefaultProvider() {
assertEquals(DefaultBaggageProvider.get(), Baggage.provider);
assertNull(Baggage.newInstance());
assertNull(Baggage.branch(null));
assertNull(Baggage.join(null, null));
assertNull(Baggage.serialize(null));
assertNull(Baggage.serialize(null, 0));
assertNull(Baggage.deserialize(null));
assertNull(Baggage.deserialize(null, 0, 0));
}
@Test
public void testStaticAPICallsIgnoreInvalidContext() {
BaggageContext invalidContext = new BaggageContext() {};
assertNull(Baggage.branch(invalidContext));
assertNull(Baggage.join(invalidContext, invalidContext));
assertNull(Baggage.serialize(invalidContext));
assertNull(Baggage.serialize(invalidContext, 0));
}
private static final class BaggageContextForTest implements BaggageContext {}
private static final class BaggageProviderForTest implements BaggageProvider<BaggageContextForTest> {
int isValid, newInstance, discard, branch, join, deserialize1, deserialize2, serialize1, serialize2;
@Override
public boolean isValid(BaggageContext baggage) {
isValid++;
return true;
}
@Override
public BaggageContextForTest newInstance() {
newInstance++;
return null;
}
@Override
public void discard(BaggageContextForTest baggage) {
discard++;
}
@Override
public BaggageContextForTest branch(BaggageContextForTest from) {
branch++;
return null;
}
@Override
public BaggageContextForTest join(BaggageContextForTest left, BaggageContextForTest right) {
join++;
return null;
}
@Override
public BaggageContextForTest deserialize(byte[] serialized, int offset, int length) {
deserialize1++;
return null;
}
@Override
public BaggageContextForTest deserialize(ByteBuffer buf) {
deserialize2++;
return null;
}
@Override
public byte[] serialize(BaggageContextForTest baggage) {
serialize1++;
return null;
}
@Override
public byte[] serialize(BaggageContextForTest baggage, int maximumSerializedSize) {
serialize2++;
return null;
}
public void reset() {
isValid = 0;
newInstance = 0;
discard = 0;
branch = 0;
join = 0;
deserialize1 = 0;
deserialize2 = 0;
serialize1 = 0;
serialize2 = 0;
}
public void expect(int isValid, int newInstance, int discard, int branch, int join, int d1, int d2, int s1, int s2) {
assertEquals(isValid, this.isValid);
assertEquals(newInstance, this.newInstance);
assertEquals(discard, this.discard);
assertEquals(branch, this.branch);
assertEquals(join, this.join);
assertEquals(d1, this.deserialize1);
assertEquals(d2, this.deserialize2);
assertEquals(s1, this.serialize1);
assertEquals(s2, this.serialize2);
reset();
}
}
@Test
public void testStaticAPICallsInvokeProvider() {
BaggageProvider<BaggageContext> originalProvider = Baggage.provider;
BaggageProviderForTest providerForTest = new BaggageProviderForTest();
Baggage.provider = BaggageProviderProxy.wrap(providerForTest);
try {
providerForTest.expect(0,0,0,0,0,0,0,0,0);
Baggage.newInstance();
providerForTest.expect(0,1,0,0,0,0,0,0,0);
Baggage.branch(null);
providerForTest.expect(1,0,0,1,0,0,0,0,0);
Baggage.join(null, null);
providerForTest.expect(2,0,0,0,1,0,0,0,0);
Baggage.discard(null);
providerForTest.expect(1,0,1,0,0,0,0,0,0);
Baggage.serialize(null);
providerForTest.expect(1,0,0,0,0,0,0,1,0);
Baggage.serialize(null, 10);
providerForTest.expect(1,0,0,0,0,0,0,0,1);
Baggage.deserialize(null);
providerForTest.expect(0,0,0,0,0,0,1,0,0);
Baggage.deserialize(null,0,0);
providerForTest.expect(0,0,0,0,0,1,0,0,0);
} finally {
Baggage.provider = originalProvider;
}
}
}
......@@ -222,7 +222,7 @@
<version>2.19.1</version>
<configuration>
<systemPropertyVariables>
<log4j.configuration>file:${project.parent.basedir}/resources/log4j-surefire.properties</log4j.configuration>
<log4j.configuration>file:${project.parent.parent.basedir}/resources/log4j-surefire.properties</log4j.configuration>
</systemPropertyVariables>
</configuration>
</plugin>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment