Commit 74bf24bf authored by Jonathan Mace's avatar Jonathan Mace

Updates to BDL-generated code, tests, and examples

parent fd908c9d
......@@ -5,7 +5,7 @@ import java.nio.ByteBuffer;
/**
* <p>
* The static methods in the {@link Baggage} class are the main entry point for manipulating {@link BaggageContext}
* instances. The methods here mirror those provided by the {@link BaggageProvider} interface.
* instances. The methods here mirror those provided by the {@link BaggageProvider} interface.
* <p>
*
* <p>
......@@ -65,6 +65,16 @@ public class Baggage {
return provider.join(left, right);
}
/**
* Deserialize the provided serialized baggage representation.
*
* @param serialized a serialized baggage
* @return a deserialized baggage instance, possibly null
*/
public static BaggageContext deserialize(byte[] serialized) {
return provider.deserialize(serialized, 0, serialized == null ? 0 : serialized.length);
}
/**
* Deserialize the provided serialized baggage representation.
*
......
......@@ -33,7 +33,8 @@ public class TestStaticAPI {
assertNull(Baggage.join(null, null));
assertNull(Baggage.serialize(null));
assertNull(Baggage.serialize(null, 0));
assertNull(Baggage.deserialize(null));
assertNull(Baggage.deserialize((byte[]) null));
assertNull(Baggage.deserialize((ByteBuffer) null));
assertNull(Baggage.deserialize(null, 0, 0));
}
......@@ -153,10 +154,12 @@ public class TestStaticAPI {
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);
Baggage.deserialize((ByteBuffer) 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);
Baggage.deserialize((byte[]) null);
providerForTest.expect(0, 0, 0, 0, 0, 1, 0, 0, 0);
} finally {
Baggage.provider = originalProvider;
}
......
package brown.tracingplane.impl;
package brown.tracingplane;
import java.util.function.BiFunction;
import java.util.function.Function;
......
......@@ -9,6 +9,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.Lists;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.BaggageListener;
import brown.tracingplane.atomlayer.AtomLayerOverflow;
import brown.tracingplane.atomlayer.Lexicographic;
import brown.tracingplane.atomlayer.StringUtils;
......@@ -16,6 +17,7 @@ import brown.tracingplane.baggageprotocol.BagKey;
import brown.tracingplane.baggageprotocol.BaggageReader;
import brown.tracingplane.baggageprotocol.BaggageWriter;
import brown.tracingplane.bdl.Bag;
import brown.tracingplane.impl.BaggageHandlerRegistry.Registrations;
/**
* <p>
......@@ -47,7 +49,7 @@ import brown.tracingplane.bdl.Bag;
* or statically in code:
*
* <pre>
* BDLContext.register(22, my.compiled.object.MyObject.class);
* BaggageHandlerRegistry.add(BagKey.indexed(22), my.compiled.object.MyObject.Handler.instance);
* </pre>
* </p>
*
......@@ -247,10 +249,11 @@ public class BDLContext implements BaggageContext {
// Parse the contents that we have handlers for
BDLContext bbcontents = null;
for (int i = 0, len = registry.keys.length; i < len; i++) {
BagKey key = registry.keys[i];
Registrations reg = registry.registrations;
for (int i = 0, len = reg.keys.length; i < len; i++) {
BagKey key = reg.keys[i];
if (reader.enter(key)) {
Bag parsed = registry.handlers[i].parse(reader);
Bag parsed = reg.handlers[i].parse(reader);
if (parsed == null) {
continue;
}
......
......@@ -7,19 +7,46 @@ import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.BaggageListener;
import brown.tracingplane.BaggageListener.BranchListener;
import brown.tracingplane.BaggageListener.JoinListener;
import brown.tracingplane.BaggageProvider;
import brown.tracingplane.atomlayer.AtomLayerSerialization;
import brown.tracingplane.baggageprotocol.BagKey;
import brown.tracingplane.baggageprotocol.BaggageReader;
import brown.tracingplane.baggageprotocol.BaggageWriter;
import brown.tracingplane.bdl.BaggageHandler;
import brown.tracingplane.impl.BaggageListener.BranchListener;
import brown.tracingplane.impl.BaggageListener.JoinListener;
import brown.tracingplane.bdl.BDLUtils;
import brown.tracingplane.bdl.Bag;
/**
* <p>
* {@link BDLContextProvider} is the primary {@link BaggageProvider} for the tracing plane. It provides a
* {@link BaggageContext} implementation that is used by BDL-generated classes.
* </p>
*
* <p>
* {@link BDLContextProvider} has several static API methods for getting and setting objects within
* {@link BaggageContext} instances. In general, you should not need to use these methods directly; instead, they are
* used by the BDL-generated accessors.
* </p>
*/
public class BDLContextProvider implements BaggageProvider<BDLContext> {
static final Logger log = LoggerFactory.getLogger(BDLContextProvider.class);
/**
* The mapping of baggage handlers to bag numbers. This is intended to be a global singleton
*/
private BaggageHandlerRegistry registry;
BDLContextProvider() {
this(BaggageHandlerRegistry.instance);
}
BDLContextProvider(BaggageHandlerRegistry registry) {
this.registry = registry;
}
@Override
public boolean isValid(BaggageContext baggage) {
return baggage == null || baggage instanceof BDLContext;
......@@ -64,6 +91,20 @@ public class BDLContextProvider implements BaggageProvider<BDLContext> {
return joinFunction.apply(left, right);
}
/**
* Additional operation provided by {@link BDLContext} to compact a context. This is a prototype, its behavior is
* datatype-dependent and generally should not be used. Its implementation is kinda hacky since I'm not sure if this
* should be incorporated into the main API yet. Currently only {@link CounterImpl} actually implements compaction.
*/
public BDLContext compact(BDLContext left, BDLContext right) {
BDLUtils.is_compaction.set(true);
try {
return joinFunction.apply(left, right);
} finally {
BDLUtils.is_compaction.set(false);
}
}
/**
* Registers a {@link JoinListener} that will be invoked any time
* {@link BDLContextProvider#join(BDLContext, BDLContext)} is invoked.
......@@ -104,24 +145,60 @@ public class BDLContextProvider implements BaggageProvider<BDLContext> {
}
/**
* The mapping of baggage handlers to bag numbers
* <p>
* This method fetches the object stored in the provided {@link BaggageContext} under the specified key, or returns
* null if there is no object present.
* </p>
*
* <p>
* This method is typically invoked by BDL-generated classes when calling the <code>getFrom(BaggageContext)</code>
* method. For example, if we generated a bag called <code>XTraceBaggage</code> and registered it to
* <code>BagKey.indexed(7)</code>, then a call to <code>get(BaggageContext, BagKey.indexed(7))</code> would return
* either null (if there is no <code>XTraceBaggage</code> instance present), or an instance of
* <code>XTraceBaggage</code>.
* </p>
*
* @param baggage a {@link BaggageContext} instance, expected to be an instance of {@link BDLContext}
* @param key the key to look up
* @return if <code>baggage</code> is an instance of {@link BDLContext}, returns the object mapped to
* <code>key</code> if there is one. Otherwise, returns null.
*/
BaggageHandlerRegistry registry = BaggageHandlerRegistry.create();
public static Bag get(BaggageContext baggage, BagKey key) {
if (!(baggage instanceof BDLContext)) {
return null;
}
return ((BDLContext) baggage).get(key);
}
/**
* Register a {@link BaggageHandler} for the specified key. In general, this mapping should be configured statically
* in config, rather than using this method.
* <p>
* Maps the specified key to the provided bag value. This method possibly modifies the provided <code>baggage</code>
* instance, or might return a new instance depending on whether <code>baggage</code> is modifiable.
* </p>
*
* <p>
* To construct a new {@link BaggageContext} with the specified mapping, simply pass <code>null</code> for
* <code>baggage</code>.
* </p>
*
* @param baggage the baggage to modify, which may be null, indicating the empty baggage.
* @param key the key to update
* @param bag the new bag to map to this key
* @return a possibly new baggage instance with the updated mapping.
*/
public void register(BagKey key, BaggageHandler<?> handler) {
if (key == null) {
log.error("Unable to register handler for null key: " + String.valueOf(handler));
return;
}
if (handler == null) {
log.error("Cannot register null handler for key " + key);
return;
public static BaggageContext set(BaggageContext baggage, BagKey key, Bag value) {
if (key != null) {
BDLContext contents;
if (baggage instanceof BDLContext) {
contents = (BDLContext) baggage;
} else {
contents = new BDLContext();
}
contents.put(key, value);
return contents;
} else {
return baggage;
}
registry = registry.add(key, handler);
}
}
......@@ -11,7 +11,7 @@ public class BDLContextProviderFactory implements BaggageProviderFactory {
@Override
public BaggageProvider<? extends BaggageContext> provider() {
return new BDLContextProvider();
return new BDLContextProvider(BaggageHandlerRegistry.instance);
}
}
......@@ -110,13 +110,5 @@ public class BDLContextUtils {
public void compact() {}
public void exit() {}
}
// This is a hack to add 'compaction' joins without deploying them to all the interfaces.
// At this point in time it's still a question of whether compact joins are useful
public static final ThreadLocal<Boolean> is_compaction = new ThreadLocal<Boolean>() {
@Override public Boolean initialValue() {
return false;
}
};
}
......@@ -18,8 +18,7 @@ import brown.tracingplane.bdl.BaggageHandler;
/**
* <p>
* {@link BaggageHandlerRegistry} manages the mapping of baggage handlers to bag numbers. The globally configured mapping of
* baggage handlers to bag numbers is maintained in {@link BDLContext#registry}.
* {@link BaggageHandlerRegistry} manages the mapping of baggage handlers to bag numbers.
* </p>
*
* <p>
......@@ -50,28 +49,138 @@ public class BaggageHandlerRegistry {
private static final String BAGS_CONFIGURATION_KEY = "bag";
/* Implementation notes: Could be volatile or atomic reference, but not trying to optimize for adding handlers at
* runtime (only at init). Could be treemap if the performance different is fine */
final BagKey[] keys;
final BaggageHandler<?>[] handlers;
static final BaggageHandlerRegistry instance = BaggageHandlerRegistry.create();
final Map<BaggageHandler<?>, BagKey> handlersToKeys = new HashMap<>();
final Map<BagKey, BaggageHandler<?>> keysToHandlers = new TreeMap<>();
Registrations registrations;
private BaggageHandlerRegistry(Registrations registrations) {
this.registrations = registrations;
}
/**
* Look up the {@link BagKey} that the specified {@link BaggageHandler} is registered to
*/
public static BagKey get(BaggageHandler<?> handler) {
return instance.doGet(handler);
}
/**
* Registers the provided key with the provided handler. It is recommended to statically configure the mapping from
* keys to handlers as part of the process configuration, rather than calling this method.
*/
public static void add(BagKey key, BaggageHandler<?> handler) {
instance.doAdd(key, handler);
}
/**
* Unregisters the handler for the specified bag key.
*/
public static void remove(BagKey key) {
instance.doRemove(key);
}
BagKey doGet(BaggageHandler<?> handler) {
return registrations.get(handler);
}
void doAdd(BagKey key, BaggageHandler<?> handler) {
if (key == null) {
log.error("Unable to register handler for null key: " + String.valueOf(handler));
} else if (handler == null) {
log.error("Cannot register null handler for key " + key);
} else {
registrations = registrations.add(key, handler);
}
}
void doRemove(BagKey key) {
registrations = registrations.remove(key);
}
static class Registrations {
/* Implementation notes: Could be volatile or atomic reference, but not trying to optimize for adding handlers
* at runtime (only at init). Could be treemap if the performance different is fine */
final BagKey[] keys;
final BaggageHandler<?>[] handlers;
final Map<BaggageHandler<?>, BagKey> handlersToKeys = new HashMap<>();
final Map<BagKey, BaggageHandler<?>> keysToHandlers = new TreeMap<>();
Registrations(Map<BagKey, BaggageHandler<?>> mapping) {
keys = new BagKey[mapping.size()];
handlers = new BaggageHandler<?>[mapping.size()];
int i = 0;
for (BagKey key : mapping.keySet()) {
BaggageHandler<?> handler = mapping.get(key);
keysToHandlers.put(key, handler);
handlersToKeys.put(handler, key);
keys[i] = key;
handlers[i] = handler;
i++;
}
}
/**
* Look up the {@link BagKey} that the specified {@link BaggageHandler} is registered to
*/
BagKey get(BaggageHandler<?> handler) {
return handlersToKeys.get(handler);
}
/**
* Registers the provided key with the provided handler into the process's default registrations. Recommended
* NOT to do this -- instead you should statically configure the registration of keys to handlers.
*
* {@link BaggageHandlerRegistry} instances are immutable, so adding a new registration will create and return a
* new {@link BaggageHandlerRegistry} instance with the additional mapping.
*/
Registrations add(BagKey key, BaggageHandler<?> handler) {
Map<BagKey, BaggageHandler<?>> newMapping = new TreeMap<>(keysToHandlers);
newMapping.put(key, handler);
return new Registrations(newMapping);
}
/**
* Unregisters the handler for the specified bag key.
*
* {@link BaggageHandlerRegistry} instances are immutable, so removing a registration will return a new
* {@link BaggageHandlerRegistry} instance with the mapping removed.
*/
Registrations remove(BagKey key) {
Map<BagKey, BaggageHandler<?>> newMapping = new TreeMap<>(keysToHandlers);
if (newMapping.remove(key) != null) {
return new Registrations(newMapping);
} else {
return this;
}
}
}
/**
* Create an empty {@link BaggageHandlerRegistry}
*/
static BaggageHandlerRegistry empty() {
return new BaggageHandlerRegistry(new Registrations(new TreeMap<>()));
}
/**
* Create a {@link BaggageHandlerRegistry} instance by parsing configured values from the default config
*/
public static BaggageHandlerRegistry create() {
static BaggageHandlerRegistry create() {
return create(ConfigFactory.load());
}
/**
* Create a {@link BaggageHandlerRegistry} instance by parsing the mappings configured in the provided {@link Config}
* Create a {@link BaggageHandlerRegistry} instance by parsing the mappings configured in the provided
* {@link Config}
*
* @param config a typesafe config
* @return a {@link BaggageHandlerRegistry} instance with handlers loaded for the configured bag keys
*/
public static BaggageHandlerRegistry create(Config config) {
static BaggageHandlerRegistry create(Config config) {
Map<BagKey, BaggageHandler<?>> mapping = new TreeMap<>();
for (Entry<String, ConfigValue> x : config.getConfig(BAGS_CONFIGURATION_KEY).entrySet()) {
String bagHandlerClassName = x.getValue().unwrapped().toString();
......@@ -85,24 +194,6 @@ public class BaggageHandlerRegistry {
mapping.put(key, handler);
}
return new BaggageHandlerRegistry(mapping);
}
private BaggageHandlerRegistry(Map<BagKey, BaggageHandler<?>> mapping) {
keys = new BagKey[mapping.size()];
handlers = new BaggageHandler<?>[mapping.size()];
int i = 0;
for (BagKey key : mapping.keySet()) {
BaggageHandler<?> handler = mapping.get(key);
keysToHandlers.put(key, handler);
handlersToKeys.put(handler, key);
keys[i] = key;
handlers[i] = handler;
i++;
}
if (mapping.size() == 0) {
log.warn("No baggage handlers are registered -- if this is unexpected, ensure `bag` is correctly configured");
} else {
......@@ -112,41 +203,7 @@ public class BaggageHandlerRegistry {
.collect(Collectors.joining("\n"));
log.info(mapping.size() + " baggage handlers registered:\n" + handlersString);
}
}
/**
* Look up the {@link BagKey} that the specified {@link BaggageHandler} is registered to
*/
public BagKey get(BaggageHandler<?> handler) {
return handlersToKeys.get(handler);
}
/**
* Registers the provided key with the provided handler into the process's default registrations. Recommended NOT to
* do this -- instead you should statically configure the registration of keys to handlers.
*
* {@link BaggageHandlerRegistry} instances are immutable, so adding a new registration will create and return a new
* {@link BaggageHandlerRegistry} instance with the additional mapping.
*/
public BaggageHandlerRegistry add(BagKey key, BaggageHandler<?> handler) {
Map<BagKey, BaggageHandler<?>> newMapping = new TreeMap<>(keysToHandlers);
newMapping.put(key, handler);
return new BaggageHandlerRegistry(newMapping);
}
/**
* Unregisters the handler for the specified bag key.
*
* {@link BaggageHandlerRegistry} instances are immutable, so removing a registration will return a new
* {@link BaggageHandlerRegistry} instance with the mapping removed.
*/
public BaggageHandlerRegistry remove(BagKey key) {
Map<BagKey, BaggageHandler<?>> newMapping = new TreeMap<>(keysToHandlers);
if (newMapping.remove(key) != null) {
return new BaggageHandlerRegistry(newMapping);
} else {
return this;
}
return new BaggageHandlerRegistry(new Registrations(mapping));
}
static Integer parseBagKey(String key, String className) {
......
package brown.tracingplane;
package brown.tracingplane.impl;
import static org.junit.Assert.assertEquals;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.junit.Test;
import brown.tracingplane.impl.BDLContext;
import brown.tracingplane.impl.BDLContextProvider;
import brown.tracingplane.impl.BaggageListener.BranchListener;
import brown.tracingplane.impl.BaggageListener.JoinListener;
import brown.tracingplane.BaggageListener.BranchListener;
import brown.tracingplane.BaggageListener.JoinListener;
public class TestBaggageListener {
......
package brown.tracingplane.impl;
import static org.junit.Assert.assertEquals;
import org.apache.log4j.BasicConfigurator;
import org.junit.Test;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
......@@ -13,17 +12,17 @@ import brown.tracingplane.bdl.Bag;
import brown.tracingplane.bdl.BaggageHandler;
public class TestBaggageRegistry {
@Test
public void testEmptyByDefault() {
BaggageHandlerRegistry defaultregistry = BaggageHandlerRegistry.create();
assertEquals(0, defaultregistry.keys.length);
assertEquals(0, defaultregistry.handlers.length);
assertEquals(0, defaultregistry.registrations.keys.length);
assertEquals(0, defaultregistry.registrations.handlers.length);
}
private static class BaggageHandlerForTest implements BaggageHandler<BagForTest> {
@Override
......@@ -49,40 +48,39 @@ public class TestBaggageRegistry {
return false;
}
}
private static BaggageHandlerForTest handler = new BaggageHandlerForTest();
private static class BagForTest implements Bag {
@Override
public BaggageHandler<?> handler() {
return handler;
}
}
@Test
public void testHandlerReflection() {
Config config = ConfigFactory.load().withValue("bag.30", ConfigValueFactory.fromAnyRef(BagForTest.class.getName()));
Config config =
ConfigFactory.load().withValue("bag.30", ConfigValueFactory.fromAnyRef(BagForTest.class.getName()));
BaggageHandlerRegistry registry = BaggageHandlerRegistry.create(config);
assertEquals(1, registry.keys.length);
assertEquals(1, registry.handlers.length);
assertEquals(BagKey.indexed(30), registry.keys[0]);
assertEquals(handler, registry.handlers[0]);
assertEquals(1, registry.registrations.keys.length);
assertEquals(1, registry.registrations.handlers.length);
assertEquals(BagKey.indexed(30), registry.registrations.keys[0]);
assertEquals(handler, registry.registrations.handlers[0]);
BaggageHandlerForTest handler2 = new BaggageHandlerForTest();
registry = registry.add(BagKey.indexed(5), handler2);
assertEquals(2, registry.keys.length);
assertEquals(2, registry.handlers.length);
assertEquals(BagKey.indexed(5), registry.keys[0]);
assertEquals(handler2, registry.handlers[0]);
assertEquals(BagKey.indexed(30), registry.keys[1]);
assertEquals(handler, registry.handlers[1]);