Commit 750ef592 authored by Jonathan Mace's avatar Jonathan Mace

Uncomitted, non-compiling code

parent 6d20e3b4
package brown.tracingplane.atomlayer;
import java.util.List;
/**
* Want to avoid having apache commons as a dependency, so implement any functions used here
*/
public class StringUtils {
public static String join(List<?> objects, String separator) {
public static String join(Iterable<?> objects, String separator) {
if (objects == null) return "";
StringBuilder b = new StringBuilder();
boolean first = true;
......
......@@ -19,6 +19,14 @@
<artifactId>bdl-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
</dependency>
</dependencies>
<build>
......
package brown.tracingplane.impl;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import brown.tracingplane.BaggageContext;
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;
public class BDLContextProvider implements BaggageProvider<BDLContext> {
static final Logger log = LoggerFactory.getLogger(BDLContextProvider.class);
@Override
public boolean isValid(BaggageContext baggage) {
return baggage == null || baggage instanceof BDLContext;
}
@Override
public BDLContext newInstance() {
return null;
}
@Override
public void discard(BDLContext baggage) {}
/**
* Implementation of branch, possibly wrapped with {@link BaggageListener} instances
*/
Function<BDLContext, BDLContext> branchFunction = ctx -> ctx == null ? null : ctx.branch();
@Override
public BDLContext branch(BDLContext from) {
return branchFunction.apply(from);
}
/**
* Registers a {@link BranchListener} that will be invoked any time {@link BDLContextProvider#branch(BDLContext)} is
* invoked.
*
* @param listener a {@link BranchListener}
*/
public void addListener(final BranchListener<BDLContext> listener) {
final Function<BDLContext, BDLContext> wrappedBranchFunction = branchFunction;
branchFunction = ctx -> listener.branch(ctx, wrappedBranchFunction);
}
/**
* Implementation of join, possibly wrapped with {@link BaggageListener} instances
*/
BiFunction<BDLContext, BDLContext, BDLContext> joinFunction = (l, r) -> l == null ? r : l.mergeWith(r);
@Override
public BDLContext join(BDLContext left, BDLContext right) {
return joinFunction.apply(left, right);
}
/**
* Registers a {@link JoinListener} that will be invoked any time
* {@link BDLContextProvider#join(BDLContext, BDLContext)} is invoked.
*
* @param listener a {@link JoinListener}
*/
public void addListener(final JoinListener<BDLContext> listener) {
final BiFunction<BDLContext, BDLContext, BDLContext> wrappedJoinFunction = joinFunction;
joinFunction = (l, r) -> listener.join(l, r, wrappedJoinFunction);
}
@Override
public BDLContext deserialize(byte[] serialized, int offset, int length) {
List<ByteBuffer> atoms = AtomLayerSerialization.deserialize(serialized, offset, length);
BaggageReader reader = BaggageReader.create(atoms);
return BDLContext.parseFrom(registry, reader);
}
@Override
public BDLContext deserialize(ByteBuffer buf) {
List<ByteBuffer> atoms = AtomLayerSerialization.deserialize(buf);
BaggageReader reader = BaggageReader.create(atoms);
return BDLContext.parseFrom(registry, reader);
}
@Override
public byte[] serialize(BDLContext baggage) {
if (baggage == null) return null;
BaggageWriter writer = baggage.serialize();
return AtomLayerSerialization.serialize(writer.atoms());
}
@Override
public byte[] serialize(BDLContext baggage, int maximumSerializedSize) {
if (baggage == null) return null;
BaggageWriter writer = baggage.serialize();
return AtomLayerSerialization.serialize(writer.atoms(), maximumSerializedSize);
}
/**
* The mapping of baggage handlers to bag numbers
*/
BaggageHandlerRegistry registry = BaggageHandlerRegistry.create();
/**
* Register a {@link BaggageHandler} for the specified key. In general, this mapping should be configured statically
* in config, rather than using this method.
*/
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;
}
registry = registry.add(key, handler);
}
}
package brown.tracingplane.impl;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.BaggageProvider;
import brown.tracingplane.BaggageProviderFactory;
/**
* {@link BaggageProviderFactory} for {@link BDLContextProvider}
*/
public class BDLContextProviderFactory implements BaggageProviderFactory {
@Override
public BaggageProvider<? extends BaggageContext> provider() {
return new BDLContextProvider();
}
}
package brown.tracingplane.impl;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.atomlayer.AtomLayerSerialization;
import brown.tracingplane.atomlayer.TypeUtils;
import brown.tracingplane.baggageprotocol.BagKey;
import brown.tracingplane.baggageprotocol.BaggageWriter;
import brown.tracingplane.bdl.Bag;
public class BDLContextUtils {
private BDLContextUtils(){}
/**
* Ideally, this is an instance method, but for now we're doing it here
*/
public static int serializedSize(BaggageContext instance) {
if (instance == null || !(instance instanceof BDLContext)) {
return 0;
} else {
return AtomLayerSerialization.serializedSize(((BDLContext)instance).serialize().atoms());
}
}
/**
* Ideally, this is an instance method, but for now we're doing it here
*/
public static int serializedSizeOfBag(Bag bag) {
if (bag != null) {
BaggageWriter writer = BaggageWriter.create();
bag.handler().serialize(writer, bag);
int size = 0;
for (ByteBuffer atom : writer.atoms()) {
size += 1 + atom.remaining();
}
return size;
}
return 0;
}
/**
* Ideally, this is an instance method, but for now we're doing it here
*/
public static int serializedSize (BagKey key, Bag bag) {
if (bag != null) {
BaggageWriter writer = BaggageWriter.create();
writer.enter(key);
bag.handler().serialize(writer, bag);
writer.exit();
int size = 0;
for (ByteBuffer atom : writer.atoms()) {
size += 1 + atom.remaining();
}
return size;
}
return 0;
}
public static Map<String, String> getSizeSummary(BaggageContext instance) {
if (instance == null || !(instance instanceof BDLContext)) {
return Collections.emptyMap();
}
return getSizeSummary((BDLContext) instance);
}
public static Map<String, String> getSizeSummary(BDLContext instance) {
if (instance == null) {
return Collections.emptyMap();
}
Map<String, String> summary = new HashMap<>();
summary.put("BaggageTotalSize", String.valueOf(serializedSize(instance)));
if (instance.bags != null) {
for (BagKey key : instance.bags.keySet()) {
Bag bag = instance.bags.get(key);
if (bag != null) {
String name = bag.getClass().getSimpleName();
summary.put(name, String.valueOf(serializedSize(key, bag)));
}
}
}
if (instance.overflowAtoms != null) {
summary.put("OverflowAtoms", TypeUtils.toHexString(instance.overflowAtoms, ","));
}
if (instance.unprocessedAtoms != null) {
summary.put("UnprocessedAtoms", TypeUtils.toHexString(instance.overflowAtoms, ","));
}
return summary;
}
/** Utility for seeing who accesses baggage and where */
public static interface BaggageAccessListener {
public boolean enter();
public void get(BagKey bagKey);
public void set(BagKey bagKey);
public void compact();
public void exit();
}
public static class NullBaggageListener implements BaggageAccessListener {
public boolean enter() { return false; }
public void get(BagKey bagKey) {}
public void set(BagKey bagKey) {}
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;
}
};
}
package brown.tracingplane.impl;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValue;
import brown.tracingplane.baggageprotocol.BagKey;
import brown.tracingplane.bdl.Bag;
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}.
* </p>
*
* <p>
* BDL-compiled objects must be registered with a static bag number in order to be used. In concept, this is similar to
* how protocols get mapped to ports (e.g., SSH maps to port 22, etc.). To register a bag, either specify it as a
* command line configuration value:
*
* <pre>
* -Dbag.22=my.compiled.object.MyObject
* </pre>
*
* or in your <code>application.conf</code>:
*
* <pre>
* bag.22 = "my.compiled.object.MyObject"
* </pre>
*
* or statically in code:
*
* <pre>
* BDLContext.register(22, my.compiled.object.MyObject.class);
* </pre>
* </p>
*/
public class BaggageHandlerRegistry {
private static final Logger log = LoggerFactory.getLogger(BaggageHandlerRegistry.class);
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;
final Map<BaggageHandler<?>, BagKey> handlersToKeys = new HashMap<>();
final Map<BagKey, BaggageHandler<?>> keysToHandlers = new TreeMap<>();
/**
* Create a {@link BaggageHandlerRegistry} instance by parsing configured values from the default config
*/
public static BaggageHandlerRegistry create() {
return create(ConfigFactory.load());
}
/**
* 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) {
Map<BagKey, BaggageHandler<?>> mapping = new TreeMap<>();
for (Entry<String, ConfigValue> x : config.getConfig(BAGS_CONFIGURATION_KEY).entrySet()) {
String bagHandlerClassName = x.getValue().unwrapped().toString();
Integer bagNumber = parseBagKey(x.getKey(), bagHandlerClassName);
if (bagNumber == null) continue;
BagKey key = BagKey.indexed(bagNumber);
BaggageHandler<?> handler = resolveHandler(bagHandlerClassName);
if (handler == null) continue;
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 {
String handlersString = mapping.entrySet().stream()
.map(e -> "\t" + e.getKey().toString() + ": " +
e.getValue().getClass().getName().toString())
.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;
}
}
static Integer parseBagKey(String key, String className) {
if (key.startsWith("\"") && key.endsWith("\"")) {
key = key.substring(1, key.length() - 1);
}
try {
return Integer.parseUnsignedInt(key);
} catch (NumberFormatException e) {
log.error("Cannot configure handler \"" + key + " = " + className +
"\" due to unparsable unsigned integer " + key);
return null;
}
}
@SuppressWarnings("unchecked")
static BaggageHandler<?> resolveHandler(String bagClassName) {
try {
Class<?> cls = Class.forName(bagClassName);
if (Bag.class.isAssignableFrom(cls)) {
return resolveHandlerForBag((Class<? extends Bag>) cls);
} else {
log.error("Unable to get BaggageHandler for non-Bag class " + cls);
}
} catch (ClassNotFoundException e) {
log.error("Cannot instantiate Bag handler " + bagClassName + ", class not found", e);
}
return null;
}
static BaggageHandler<?> resolveHandlerForBag(Class<? extends Bag> cls) {
try {
Constructor<? extends Bag> constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance().handler();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
log.error("Unable to instantiate Bag class " + cls, e);
}
return null;
}
}
package brown.tracingplane.impl;
import java.util.function.BiFunction;
import java.util.function.Function;
import brown.tracingplane.BaggageContext;
import brown.tracingplane.BaggageProvider;
/**
* A {@link BaggageListener} is a callback handler that is invoked whenever core {@link BaggageProvider} functions
* (branch, merge, etc.) are invoked. Baggage Listeners are able to manipulate baggage instances before and after the
* functions are called.
*
* A {@link BaggageProvider} must support adding {@link BaggageListener}s.
*/
public final class BaggageListener {
/** BaggageListeners is not instantiable */
private BaggageListener() {}
/**
* Wraps calls to {@link BaggageProvider#branch(BaggageContext)} so that the baggage instance(s) can be modified.
*/
@FunctionalInterface
public static interface BranchListener<B extends BaggageContext> {
/**
* Invoked when {@link BaggageProvider#branch(BaggageContext)} is called. <code>wrapped</code> is the default
* implementation of branch which should be called. This method can optionally perform additional logic before
* or after invocation, or override its behavior completely.
*
* @param from a baggage context, possibly null
* @param wrapped the default {@link BaggageProvider#branch(BaggageContext)} function
* @return a baggage context branched from <code>from</code>, possibly null
*/
public B branch(B from, Function<B, B> wrapped);
}
/**
* Wraps calls to {@link BaggageProvider#join(BaggageContext, BaggageContext)} so that baggage instance(s) can be
* modified.
*/
@FunctionalInterface
public static interface JoinListener<B extends BaggageContext> {
/**
* Invoked when {@link BaggageProvider#join(BaggageContext, BaggageContext)} is called. <code>wrapped</code> is
* the default implementation of join which should be called. This method can optionally perform additional
* logic before or after invocation, or override its behavior completely.
*
* @param left a {@link BaggageContext} instance, possibly null
* @param right a {@link BaggageContext} instance, possibly null
* @param next the default {@link BaggageProvider#join(BaggageContext, BaggageContext)} function
* @return a baggage context with merged contents from <code>left</code> and <code>right</code>
*/
public B join(B left, B right, BiFunction<B, B, B> wrapped);
}
}
# The mapping of bags to baggage handlers is done via the 'bags' configuration key
bag {
# Example registration:
# 10 = "brown.xtrace.XTraceBaggage"
}
\ No newline at end of file
package brown.tracingplane;
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;