diff --git a/bosk-core/src/main/java/io/vena/bosk/Bosk.java b/bosk-core/src/main/java/io/vena/bosk/Bosk.java index 1795d959..a93a4e4e 100644 --- a/bosk-core/src/main/java/io/vena/bosk/Bosk.java +++ b/bosk-core/src/main/java/io/vena/bosk/Bosk.java @@ -73,7 +73,7 @@ * * @param The type of the state tree's root node */ -public class Bosk { +public class Bosk implements BoskInfo { @Getter private final String name; @Getter private final Identifier instanceID = Identifier.from(randomUUID().toString()); @Getter private final BoskDriver driver; @@ -111,10 +111,20 @@ public Bosk(String name, Type rootType, DefaultRootFunction defaultRootFuncti throw new IllegalArgumentException("Invalid root type " + rootType + ": " + e.getMessage(), e); } - // We do this last because the driver factory is allowed to do such things - // as create References, so it needs the rest of the initialization to - // have completed already. - this.driver = driverFactory.build(this, this.localDriver); + // Rather than pass `this` while it's still under construction, + // pass another object that contains the things that are available + // at this point. + // + UnderConstruction boskInfo = new UnderConstruction<>( + name, instanceID, rootRef, this::registerHooks + ); + + // We do this as late as possible because the driver factory is allowed + // to do such things as create References, so it needs the rest of the + // initialization to have completed already. + // + this.driver = driverFactory.build(boskInfo, this.localDriver); + try { this.currentRoot = requireNonNull(driver.initialRoot(rootType)); } catch (InvalidTypeException | IOException | InterruptedException e) { @@ -133,11 +143,27 @@ public Bosk(String name, Type rootType, R defaultRoot, DriverFactory driverFa this(name, rootType, b->defaultRoot, driverFactory); } + record UnderConstruction( + String name, + Identifier instanceID, + RootReference rootReference, + RegisterHooksMethod m + ) implements BoskInfo { + @Override + public void registerHooks(Object receiver) throws InvalidTypeException { + m.registerHooks(receiver); + } + } + + private interface RegisterHooksMethod { + void registerHooks(Object receiver) throws InvalidTypeException; + } + /** * You can use Bosk::simpleDriver as the * driverFactory if you don't want any additional driver functionality. */ - public static BoskDriver simpleDriver(@SuppressWarnings("unused") Bosk bosk, BoskDriver downstream) { + public static BoskDriver simpleDriver(@SuppressWarnings("unused") BoskInfo boskInfo, BoskDriver downstream) { return downstream; } @@ -499,6 +525,7 @@ public void registerHook(String name, @NonNull Reference scope, @NonNull localDriver.triggerEverywhere(reg); } + @Override public void registerHooks(Object receiver) throws InvalidTypeException { HookRegistrar.registerHooks(receiver, this); } @@ -1000,7 +1027,7 @@ public void forEachValue(BiConsumer action, BindingEnviro * correspond to an {@link Identifier} that can be looked up in an * object that implements {@link EnumerableByIdentifier}. (We are * not offering to use reflection to look up object fields by name here.) - * + *

* TODO: This is not currently checked or enforced; it will just cause confusing crashes. * It should throw {@link InvalidTypeException} at the time the Reference is created. */ diff --git a/bosk-core/src/main/java/io/vena/bosk/BoskInfo.java b/bosk-core/src/main/java/io/vena/bosk/BoskInfo.java new file mode 100644 index 00000000..6ac5188d --- /dev/null +++ b/bosk-core/src/main/java/io/vena/bosk/BoskInfo.java @@ -0,0 +1,14 @@ +package io.vena.bosk; + +import io.vena.bosk.exceptions.InvalidTypeException; + +/** + * Provides access to a subset of bosk functionality that is available at the time + * the {@link BoskDriver} is built, before the {@link Bosk} itself is fully initialized. + */ +public interface BoskInfo { + String name(); + Identifier instanceID(); + RootReference rootReference(); + void registerHooks(Object receiver) throws InvalidTypeException; +} diff --git a/bosk-core/src/main/java/io/vena/bosk/DriverFactory.java b/bosk-core/src/main/java/io/vena/bosk/DriverFactory.java index 68eea86a..9dbee0fe 100644 --- a/bosk-core/src/main/java/io/vena/bosk/DriverFactory.java +++ b/bosk-core/src/main/java/io/vena/bosk/DriverFactory.java @@ -1,5 +1,5 @@ package io.vena.bosk; public interface DriverFactory { - BoskDriver build(Bosk bosk, BoskDriver downstream); + BoskDriver build(BoskInfo boskInfo, BoskDriver downstream); } diff --git a/bosk-core/src/main/java/io/vena/bosk/DriverStack.java b/bosk-core/src/main/java/io/vena/bosk/DriverStack.java index 5bef9a2d..94b13abd 100644 --- a/bosk-core/src/main/java/io/vena/bosk/DriverStack.java +++ b/bosk-core/src/main/java/io/vena/bosk/DriverStack.java @@ -23,10 +23,10 @@ public interface DriverStack extends DriverFactory { static DriverStack of(DriverFactory...factories) { return new DriverStack() { @Override - public BoskDriver build(Bosk bosk, BoskDriver downstream) { + public BoskDriver build(BoskInfo boskInfo, BoskDriver downstream) { BoskDriver result = downstream; for (int i = factories.length - 1; i >= 0; i--) { - result = factories[i].build(bosk, result); + result = factories[i].build(boskInfo, result); } return result; } diff --git a/bosk-core/src/main/java/io/vena/bosk/SerializationPlugin.java b/bosk-core/src/main/java/io/vena/bosk/SerializationPlugin.java index 0b10ea94..23a4d3e6 100644 --- a/bosk-core/src/main/java/io/vena/bosk/SerializationPlugin.java +++ b/bosk-core/src/main/java/io/vena/bosk/SerializationPlugin.java @@ -95,8 +95,8 @@ public final DeserializationScope entryDeserializationScope(Identifier entryID) public final DeserializationScope nodeFieldDeserializationScope(Class nodeClass, String fieldName) { DeserializationPath annotation = infoFor(nodeClass).annotatedParameters_DeserializationPath.get(fieldName); + DeserializationScope outerScope = currentScope.get(); if (annotation == null) { - DeserializationScope outerScope = currentScope.get(); DeserializationScope newScope = new NestedDeserializationScope( outerScope, outerScope.path().then(fieldName), @@ -104,7 +104,6 @@ public final DeserializationScope nodeFieldDeserializationScope(Class nodeCla currentScope.set(newScope); return newScope; } else { - DeserializationScope outerScope = currentScope.get(); try { Path path = Path .parseParameterized(annotation.value()) @@ -180,13 +179,13 @@ public void close() { * supplied where possible, such as Optional.empty() and * {@link Enclosing} references. */ - public final List parameterValueList(Class nodeClass, Map parameterValuesByName, LinkedHashMap parametersByName, Bosk bosk) { + public final List parameterValueList(Class nodeClass, Map parameterValuesByName, LinkedHashMap parametersByName, BoskInfo boskInfo) { List parameterValues = new ArrayList<>(); for (Entry entry: parametersByName.entrySet()) { String name = entry.getKey(); Parameter parameter = entry.getValue(); Class type = parameter.getType(); - Reference implicitReference = findImplicitReferenceIfAny(nodeClass, parameter, bosk); + Reference implicitReference = findImplicitReferenceIfAny(nodeClass, parameter, boskInfo); Object value = parameterValuesByName.remove(name); if (value == null) { @@ -204,7 +203,7 @@ public final List parameterValueList(Class nodeClass, Map enclosingRef; try { - enclosingRef = bosk.rootReference().then(Object.class, path.truncatedBy(1)); + enclosingRef = boskInfo.rootReference().then(Object.class, path.truncatedBy(1)); } catch (InvalidTypeException e) { throw new AssertionError("Non-empty path must have an enclosing reference: " + path, e); } @@ -285,13 +284,13 @@ private void initializePolyfills(Reference ref, } } - private Reference findImplicitReferenceIfAny(Class nodeClass, Parameter parameter, Bosk bosk) { + private Reference findImplicitReferenceIfAny(Class nodeClass, Parameter parameter, BoskInfo boskInfo) { if (isSelfReference(nodeClass, parameter)) { Class targetClass = rawClass(parameterType(parameter.getParameterizedType(), Reference.class, 0)); - return selfReference(targetClass, bosk); + return selfReference(targetClass, boskInfo); } else if (isEnclosingReference(nodeClass, parameter)) { Class targetClass = rawClass(parameterType(parameter.getParameterizedType(), Reference.class, 0)); - Reference selfRef = selfReference(Object.class, bosk); + Reference selfRef = selfReference(Object.class, boskInfo); try { return selfRef.enclosingReference(targetClass); } catch (InvalidTypeException e) { @@ -305,10 +304,10 @@ private Reference findImplicitReferenceIfAny(Class nodeClass, Parameter pa } } - private Reference selfReference(Class targetClass, Bosk bosk) throws AssertionError { + private Reference selfReference(Class targetClass, BoskInfo boskInfo) throws AssertionError { Path currentPath = currentScope.get().path(); try { - return bosk.rootReference().then(targetClass, currentPath); + return boskInfo.rootReference().then(targetClass, currentPath); } catch (InvalidTypeException e) { throw new UnexpectedPathException("currentDeserializationPath should be valid: \"" + currentPath + "\"", e); } diff --git a/bosk-core/src/main/java/io/vena/bosk/drivers/DiagnosticScopeDriver.java b/bosk-core/src/main/java/io/vena/bosk/drivers/DiagnosticScopeDriver.java index 0a4bc23b..39cd5258 100644 --- a/bosk-core/src/main/java/io/vena/bosk/drivers/DiagnosticScopeDriver.java +++ b/bosk-core/src/main/java/io/vena/bosk/drivers/DiagnosticScopeDriver.java @@ -26,7 +26,7 @@ public class DiagnosticScopeDriver implements BoskDrive final Function scopeSupplier; public static DriverFactory factory(Function scopeSupplier) { - return (b,d) -> new DiagnosticScopeDriver<>(d, b.diagnosticContext(), scopeSupplier); + return (b,d) -> new DiagnosticScopeDriver<>(d, b.rootReference().diagnosticContext(), scopeSupplier); } @Override diff --git a/bosk-core/src/main/java/io/vena/bosk/drivers/MirroringDriver.java b/bosk-core/src/main/java/io/vena/bosk/drivers/MirroringDriver.java index 245be515..9bd1e2e2 100644 --- a/bosk-core/src/main/java/io/vena/bosk/drivers/MirroringDriver.java +++ b/bosk-core/src/main/java/io/vena/bosk/drivers/MirroringDriver.java @@ -25,7 +25,7 @@ public class MirroringDriver implements BoskDriver { * Causes updates to be applied both to mirror and to the downstream driver. */ public static DriverFactory targeting(Bosk mirror) { - return (bosk, downstream) -> new ForwardingDriver<>(asList( + return (boskInfo, downstream) -> new ForwardingDriver<>(asList( new MirroringDriver<>(mirror), downstream )); diff --git a/bosk-core/src/test/java/io/vena/bosk/BoskConstructorTest.java b/bosk-core/src/test/java/io/vena/bosk/BoskConstructorTest.java index db1e7ce1..675494e9 100644 --- a/bosk-core/src/test/java/io/vena/bosk/BoskConstructorTest.java +++ b/bosk-core/src/test/java/io/vena/bosk/BoskConstructorTest.java @@ -128,16 +128,6 @@ void defaultRoot_matches() { } } - @Test - void readContextDuringDriverFactory_throws() { - assertThrows(IllegalStateException.class, ()->{ - new Bosk<>("readContext", SimpleTypes.class, newEntity(), (b,d) -> { - try (val __ = b.readContext()) {} - return d; - }); - }); - } - //////////////// // // Helpers diff --git a/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonCompiler.java b/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonCompiler.java index 0b1341cc..f12b1b1f 100644 --- a/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonCompiler.java +++ b/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonCompiler.java @@ -11,7 +11,7 @@ import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.vena.bosk.Bosk; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Catalog; import io.vena.bosk.Entity; import io.vena.bosk.Phantom; @@ -66,7 +66,7 @@ final class JacksonCompiler { * * @return a newly compiled {@link CompiledSerDes} for values of the given nodeType. */ - public CompiledSerDes compiled(JavaType nodeType, Bosk bosk, FieldModerator moderator) { + public CompiledSerDes compiled(JavaType nodeType, BoskInfo boskInfo, FieldModerator moderator) { try { // Record that we're compiling this one to avoid infinite recursion compilationsInProgress.get().addLast(nodeType); @@ -89,7 +89,7 @@ public CompiledSerDes compiled(JavaType nodeType, Bosk bosk, FieldMode // Return a CodecWrapper for the codec LinkedHashMap parametersByName = new LinkedHashMap<>(); parameters.forEach(p -> parametersByName.put(p.getName(), p)); - return new CodecWrapper<>(codec, bosk, nodeType, parametersByName, moderator); + return new CodecWrapper<>(codec, boskInfo, nodeType, parametersByName, moderator); } finally { Type removed = compilationsInProgress.get().removeLast(); assert removed.equals(nodeType); @@ -384,7 +384,7 @@ public void generateFieldWrite(String name, ClassBuilder cb, LocalVariabl @EqualsAndHashCode(callSuper = false) private class CodecWrapper implements CompiledSerDes { Codec codec; - Bosk bosk; + BoskInfo boskInfo; JavaType nodeJavaType; LinkedHashMap parametersByName; FieldModerator moderator; @@ -411,7 +411,7 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOExcepti // because we need to tolerate the fields arriving in arbitrary order. Map valueMap = jacksonPlugin.gatherParameterValuesByName(nodeJavaType, parametersByName, moderator, p, ctxt); - List parameterValues = jacksonPlugin.parameterValueList(nodeJavaType.getRawClass(), valueMap, parametersByName, bosk); + List parameterValues = jacksonPlugin.parameterValueList(nodeJavaType.getRawClass(), valueMap, parametersByName, boskInfo); @SuppressWarnings("unchecked") T result = (T)codec.instantiateFrom(parameterValues); diff --git a/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonPlugin.java b/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonPlugin.java index 48c72910..be9c1ba3 100644 --- a/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonPlugin.java +++ b/bosk-jackson/src/main/java/io/vena/bosk/jackson/JacksonPlugin.java @@ -21,7 +21,7 @@ import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.MapType; import com.fasterxml.jackson.databind.type.TypeFactory; -import io.vena.bosk.Bosk; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Catalog; import io.vena.bosk.Entity; import io.vena.bosk.Identifier; @@ -72,21 +72,21 @@ public final class JacksonPlugin extends SerializationPlugin { private final JacksonCompiler compiler = new JacksonCompiler(this); - public BoskJacksonModule moduleFor(Bosk bosk) { + public BoskJacksonModule moduleFor(BoskInfo boskInfo) { return new BoskJacksonModule() { @Override public void setupModule(SetupContext context) { - context.addSerializers(new BoskSerializers(bosk)); - context.addDeserializers(new BoskDeserializers(bosk)); + context.addSerializers(new BoskSerializers(boskInfo)); + context.addDeserializers(new BoskDeserializers(boskInfo)); } }; } private final class BoskSerializers extends Serializers.Base { - private final Bosk bosk; + private final BoskInfo boskInfo; - public BoskSerializers(Bosk bosk) { - this.bosk = bosk; + public BoskSerializers(BoskInfo boskInfo) { + this.boskInfo = boskInfo; } @Override @@ -122,7 +122,7 @@ public JsonSerializer findSerializer(SerializationConfig config, JavaType typ } private JsonSerializer derivedRecordSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) { - return derivedRecordSerDes(type, beanDesc, bosk).serializer(config); + return derivedRecordSerDes(type, beanDesc, boskInfo).serializer(config); } private JsonSerializer> catalogSerializer(SerializationConfig config, BeanDescription beanDesc) { @@ -210,7 +210,7 @@ public void serialize(SideTable value, JsonGenerator gen, Serial private JsonSerializer stateTreeNodeSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) { StateTreeNodeFieldModerator moderator = new StateTreeNodeFieldModerator(type); - return compiler.compiled(type, bosk, moderator).serializer(config); + return compiler.compiled(type, boskInfo, moderator).serializer(config); } private JsonSerializer> mapValueSerializer(SerializationConfig config, BeanDescription beanDesc) { @@ -243,10 +243,10 @@ public JsonSerializer findMapSerializer(SerializationConfig config, MapType t } private final class BoskDeserializers extends Deserializers.Base { - private final Bosk bosk; + private final BoskInfo boskInfo; - public BoskDeserializers(Bosk bosk) { - this.bosk = bosk; + public BoskDeserializers(BoskInfo boskInfo) { + this.boskInfo = boskInfo; } @Override @@ -284,7 +284,7 @@ public JsonDeserializer findBeanDeserializer(JavaType type, DeserializationCo } private JsonDeserializer derivedRecordDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) { - return derivedRecordSerDes(type, beanDesc, bosk).deserializer(config); + return derivedRecordSerDes(type, beanDesc, boskInfo).deserializer(config); } private JsonDeserializer> catalogDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) { @@ -350,7 +350,7 @@ private JsonDeserializer> referenceDeserializer(JavaType type, Dese @Override public Reference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { try { - return bosk.rootReference().then(Object.class, Path.parse(p.getText())); + return boskInfo.rootReference().then(Object.class, Path.parse(p.getText())); } catch (InvalidTypeException e) { throw new UnexpectedPathException(e); } @@ -430,7 +430,7 @@ public SideTable deserialize(JsonParser p, DeserializationContex private JsonDeserializer stateTreeNodeDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) { StateTreeNodeFieldModerator moderator = new StateTreeNodeFieldModerator(type); - return compiler.compiled(type, bosk, moderator).deserializer(config); + return compiler.compiled(type, boskInfo, moderator).deserializer(config); } private JsonDeserializer> listValueDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc) { @@ -539,7 +539,7 @@ private LinkedHashMap readMapEntries(JsonParser p, JsonDeseri private static final JavaType CATALOG_REF_TYPE = TypeFactory.defaultInstance().constructType(new TypeReference< Reference>>() {}); - private CompiledSerDes derivedRecordSerDes(JavaType objType, BeanDescription beanDesc, Bosk bosk) { + private CompiledSerDes derivedRecordSerDes(JavaType objType, BeanDescription beanDesc, BoskInfo boskInfo) { // Check for special cases Class objClass = objType.getRawClass(); if (ListValue.class.isAssignableFrom(objClass)) { // TODO: MapValue? @@ -555,7 +555,7 @@ private CompiledSerDes derivedRecordSerDes(JavaType objType, BeanDescript // Default DerivedRecord handling DerivedRecordFieldModerator moderator = new DerivedRecordFieldModerator(objType); - return compiler.compiled(objType, bosk, moderator); + return compiler.compiled(objType, boskInfo, moderator); } diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/BsonPlugin.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/BsonPlugin.java index c408e997..b8b14d1c 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/BsonPlugin.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/BsonPlugin.java @@ -1,6 +1,6 @@ package io.vena.bosk.drivers.mongo; -import io.vena.bosk.Bosk; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Catalog; import io.vena.bosk.Entity; import io.vena.bosk.Identifier; @@ -90,36 +90,36 @@ private static MethodHandle computeFactoryHandle(Constructor constructor) thr * *

* In response to this shortcoming, you can access {@link Codec}s for any - * type using {@link #getCodec(Type, Class, CodecRegistry, Bosk)} + * type using {@link #getCodec(Type, Class, CodecRegistry, BoskInfo)} */ - public CodecProvider codecProviderFor(Bosk bosk) { + public CodecProvider codecProviderFor(BoskInfo boskInfo) { return new CodecProvider() { public Codec get(Class targetClass, CodecRegistry registry) { // Without generic type info, we just use the class as the type; // this will throw IllegalArgumentException if that's insufficient - return getCodec(targetClass, targetClass, registry, bosk); + return getCodec(targetClass, targetClass, registry, boskInfo); } }; } /** - * Like {@link #codecProviderFor(Bosk) codecProviderFor(bosk)}{@link + * Like {@link #codecProviderFor(BoskInfo) codecProviderFor(boskInfo)}{@link * CodecProvider#get(Class, CodecRegistry) .get(targetType, registry)} * except this works more broadly because it can accept a {@link * ParameterizedType} for generic classes. * - * @param root type of bosk + * @param root type of boskInfo * @param targetClass must match targetType. This is provided only to help Java do type inference and avoid ugly and unnecessary type casts. */ @SuppressWarnings("unchecked") - public Codec getCodec(Type targetType, Class targetClass, CodecRegistry registry, Bosk bosk) { + public Codec getCodec(Type targetType, Class targetClass, CodecRegistry registry, BoskInfo boskInfo) { if (rawClass(targetType) != targetClass) { throw new IllegalArgumentException("Type does not match Class " + targetClass.getSimpleName() + ": " + targetType); } Codec result = (Codec) memoizedCodecs.get(targetType); if (result == null) { - result = computeCodec(targetType, targetClass, registry, bosk); + result = computeCodec(targetType, targetClass, registry, boskInfo); if (result != null) { memoizedCodecs.putIfAbsent(targetType, result); } @@ -132,8 +132,8 @@ public Codec getCodec(Type targetType, Class * if required, and if that fails, falls back to the registry. */ @SuppressWarnings("unused") // GET_ANY_CODEC - private Codec getAnyCodec(Type targetType, Class targetClass, CodecRegistry registry, Bosk bosk) { - Codec result = getCodec(targetType, targetClass, registry, bosk); + private Codec getAnyCodec(Type targetType, Class targetClass, CodecRegistry registry, BoskInfo boskInfo) { + Codec result = getCodec(targetType, targetClass, registry, boskInfo); if (result == null) { return requireNonNull(registry.get(targetClass), "Codec required for " + targetType); } else { @@ -142,7 +142,7 @@ private Codec getAnyCodec(Type targetType, Class } @SuppressWarnings({ "unchecked", "rawtypes" }) // This method is trusted to handle types properly, so other methods can have good strong type checking - private Codec computeCodec(Type targetType, Class targetClass, CodecRegistry registry, Bosk bosk) { + private Codec computeCodec(Type targetType, Class targetClass, CodecRegistry registry, BoskInfo boskInfo) { // Classes that we can handle without type parameter info // if (Identifier.class.isAssignableFrom(targetClass)) { @@ -150,7 +150,7 @@ private Codec computeCodec(Type targetType, Class targetClass, CodecRegistry reg } else if (ListingEntry.class.isAssignableFrom(targetClass)) { return listingEntryCodec(); } else if (Reference.class.isAssignableFrom(targetClass)) { - return referenceCodec(bosk); + return referenceCodec(boskInfo); } else if (Enum.class.isAssignableFrom(targetClass)) { // Bosk explicitly supports enums return enumCodec(targetClass); @@ -158,15 +158,15 @@ private Codec computeCodec(Type targetType, Class targetClass, CodecRegistry reg return listingCodec(targetClass, registry); } else if (StateTreeNode.class.isAssignableFrom(targetClass)) { // TODO: What about generic node classes? - return stateTreeNodeCodec(targetClass, registry, bosk); + return stateTreeNodeCodec(targetClass, registry, boskInfo); } else if (Catalog.class.isAssignableFrom(targetClass)) { - return catalogCodec(targetType, targetClass, registry, bosk); + return catalogCodec(targetType, targetClass, registry, boskInfo); } else if (SideTable.class.isAssignableFrom(targetClass)) { - return sideTableCodec(targetType, targetClass, registry, bosk); + return sideTableCodec(targetType, targetClass, registry, boskInfo); } else if (ListValue.class.isAssignableFrom(targetClass)) { - return listValueCodec(targetType, targetClass, registry, bosk); + return listValueCodec(targetType, targetClass, registry, boskInfo); } else if (MapValue.class.isAssignableFrom(targetClass)) { - return mapValueCodec(targetType, targetClass, registry, bosk); + return mapValueCodec(targetType, targetClass, registry, boskInfo); } else if (Optional.class.isAssignableFrom(targetClass)) { // Optional.empty() can't be serialized on its own because the field name itself must also be omitted throw new IllegalArgumentException("Cannot serialize an Optional on its own; only as a field of another object"); @@ -190,7 +190,7 @@ private Codec computeCodec(Type targetType, Class targetClass, CodecRegistry reg )); private static Codec identifierCodec() { - return new Codec() { + return new Codec<>() { @Override public Class getEncoderClass() { return Identifier.class; } @Override @@ -206,7 +206,7 @@ public Identifier decode(BsonReader reader, DecoderContext decoderContext) { } private static Codec listingEntryCodec() { - return new Codec() { + return new Codec<>() { @Override public Class getEncoderClass() { return ListingEntry.class; } @Override @@ -227,7 +227,7 @@ public ListingEntry decode(BsonReader reader, DecoderContext decoderContext) { } private static > Codec enumCodec(Class enumClass) { - return new Codec() { + return new Codec<>() { @Override public Class getEncoderClass() { return enumClass; } @Override @@ -245,7 +245,7 @@ public E decode(BsonReader reader, DecoderContext decoderContext) { private static Codec> listingCodec(Class> targetClass, CodecRegistry registry) { @SuppressWarnings("rawtypes") Codec referenceCodec = registry.get(Reference.class); - return new Codec>() { + return new Codec<>() { @Override public Class> getEncoderClass() { return targetClass; } @Override @@ -296,12 +296,12 @@ public Listing decode(BsonReader reader, DecoderContext decoderContext) { }; } - private Codec> mapValueCodec(Type mapValueType, Class> targetClass, CodecRegistry registry, Bosk bosk) { + private Codec> mapValueCodec(Type mapValueType, Class> targetClass, CodecRegistry registry, BoskInfo boskInfo) { Type valueType = parameterType(mapValueType, MapValue.class, 0); @SuppressWarnings("unchecked") Class valueClass = (Class) rawClass(valueType); - Codec valueCodec = getCodec(valueType, valueClass, registry, bosk); - return new Codec>() { + Codec valueCodec = getCodec(valueType, valueClass, registry, boskInfo); + return new Codec<>() { @Override public Class> getEncoderClass() { @@ -337,13 +337,13 @@ public MapValue decode(BsonReader reader, DecoderContext decoderContext) { }; } - private Codec> listValueCodec(Type listValueType, Class> targetClass, CodecRegistry registry, Bosk bosk) { + private Codec> listValueCodec(Type listValueType, Class> targetClass, CodecRegistry registry, BoskInfo boskInfo) { Constructor> ctor = theOnlyConstructorFor(targetClass); Type entryType = parameterType(listValueType, ListValue.class, 0); @SuppressWarnings("unchecked") Class entryClass = (Class) rawClass(entryType); - Codec entryCodec = getCodec(entryType, entryClass, registry, bosk); - return new Codec>() { + Codec entryCodec = getCodec(entryType, entryClass, registry, boskInfo); + return new Codec<>() { @Override public Class> getEncoderClass() { @@ -378,8 +378,8 @@ public ListValue decode(BsonReader reader, DecoderContext decoderContext) { } - private static Codec> referenceCodec(Bosk bosk) { - return new Codec>() { + private static Codec> referenceCodec(BoskInfo boskInfo) { + return new Codec<>() { @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public Class> getEncoderClass() { return (Class)Reference.class; } @@ -392,7 +392,7 @@ public void encode(BsonWriter writer, Reference value, EncoderContext encoder public Reference decode(BsonReader reader, DecoderContext decoderContext) { String urlEncoded = reader.readString(); try { - return bosk.rootReference().then(Object.class, Path.parse(urlEncoded)); + return boskInfo.rootReference().then(Object.class, Path.parse(urlEncoded)); } catch (InvalidTypeException e) { throw new UnexpectedPathException(e); } @@ -400,16 +400,16 @@ public Reference decode(BsonReader reader, DecoderContext decoderContext) { }; } - private Codec stateTreeNodeCodec(Class nodeClass, CodecRegistry registry, Bosk bosk) { + private Codec stateTreeNodeCodec(Class nodeClass, CodecRegistry registry, BoskInfo boskInfo) { // Pre-compute some reflection-based stuff // Constructor constructor = theOnlyConstructorFor(nodeClass); LinkedHashMap parametersByName = Stream.of(constructor.getParameters()).collect(toMap(Parameter::getName, p->p, (x,y)->{ throw new BsonFormatException("Two parameters with same name \"" + x.getName() + "\": " + x + "; " + y); }, LinkedHashMap::new)); - MethodHandle writerHandle = computeAllFieldsWriterHandle(nodeClass, parametersByName, registry, bosk); + MethodHandle writerHandle = computeAllFieldsWriterHandle(nodeClass, parametersByName, registry, boskInfo); MethodHandle factoryHandle = computeFactoryHandle(constructor); - return new Codec() { + return new Codec<>() { @Override public void encode(BsonWriter writer, T value, EncoderContext encoderContext) { writer.writeStartDocument(); @@ -425,9 +425,9 @@ public void encode(BsonWriter writer, T value, EncoderContext encoderContext) { @SuppressWarnings("unchecked") public T decode(BsonReader reader, DecoderContext decoderContext) { reader.readStartDocument(); - Map parameterValuesByName = gatherParameterValuesByName(nodeClass, parametersByName, reader, decoderContext, registry, bosk); + Map parameterValuesByName = gatherParameterValuesByName(nodeClass, parametersByName, reader, decoderContext, registry, boskInfo); reader.readEndDocument(); - List parameterValues = parameterValueList(nodeClass, parameterValuesByName, parametersByName, bosk); + List parameterValues = parameterValueList(nodeClass, parameterValuesByName, parametersByName, boskInfo); try { return (T) factoryHandle.invoke(parameterValues.toArray()); } catch (Throwable e) { @@ -439,17 +439,17 @@ public T decode(BsonReader reader, DecoderContext decoderContext) { }; } - private Codec> catalogCodec(Type catalogType, Class> catalogClass, CodecRegistry registry, Bosk bosk) { + private Codec> catalogCodec(Type catalogType, Class> catalogClass, CodecRegistry registry, BoskInfo boskInfo) { Type entryType = parameterType(catalogType, Catalog.class, 0); @SuppressWarnings("unchecked") Class entryClass = (Class) rawClass(entryType).asSubclass(Entity.class); - Codec entryCodec = getCodec(entryType, entryClass, registry, bosk); - return new Codec>() { + Codec entryCodec = getCodec(entryType, entryClass, registry, boskInfo); + return new Codec<>() { @Override public Class> getEncoderClass() { return catalogClass; } @Override public void encode(BsonWriter writer, Catalog value, EncoderContext encoderContext) { - MethodHandle fieldWriter = catalogWriterHandle(entryClass, registry, bosk); + MethodHandle fieldWriter = catalogWriterHandle(entryClass, registry); try { fieldWriter.invoke(value, writer, encoderContext); } catch (Throwable e) { @@ -485,29 +485,29 @@ public Catalog decode(BsonReader reader, DecoderContext decoderContext) { return result; } - private MethodHandle catalogWriterHandle(Class entryClass, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle catalogWriterHandle(Class entryClass, CodecRegistry codecRegistry) { // Curry in the codec suppliers return collectArguments( WRITE_CATALOG, - 0, codecSupplierHandle(entryClass, codecRegistry, bosk)); + 0, codecSupplierHandle(entryClass, codecRegistry, boskInfo)); } }; } - private Codec> sideTableCodec(Type sideTableType, Class> sideTableClass, CodecRegistry registry, Bosk bosk) { + private Codec> sideTableCodec(Type sideTableType, Class> sideTableClass, CodecRegistry registry, BoskInfo boskInfo) { Type valueType = parameterType(sideTableType, SideTable.class, 1); @SuppressWarnings("unchecked") Class valueClass = (Class) rawClass(valueType); - Codec valueCodec = getCodec(valueType, valueClass, registry, bosk); + Codec valueCodec = getCodec(valueType, valueClass, registry, boskInfo); @SuppressWarnings("rawtypes") - Codec referenceCodec = getCodec(Reference.class, Reference.class, registry, bosk); + Codec referenceCodec = getCodec(Reference.class, Reference.class, registry, boskInfo); - return new Codec>() { + return new Codec<>() { @Override public Class> getEncoderClass() { return sideTableClass; } @Override public void encode(BsonWriter writer, SideTable value, EncoderContext encoderContext) { - MethodHandle fieldWriter = sideTableWriterHandle(valueType, registry, bosk); + MethodHandle fieldWriter = sideTableWriterHandle(valueType, registry); try { fieldWriter.invoke(value, writer, encoderContext); } catch (Throwable e) { @@ -545,12 +545,12 @@ public SideTable decode(BsonReader reader, DecoderContext decoderContext) return SideTable.fromOrderedMap(domain, valuesById); } - private MethodHandle sideTableWriterHandle(Type valueType, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle sideTableWriterHandle(Type valueType, CodecRegistry codecRegistry) { // Curry in the codec suppliers return collectArguments(collectArguments( WRITE_SIDE_TABLE, - 0, codecSupplierHandle(Reference.class, codecRegistry, bosk)), - 0, codecSupplierHandle(valueType, codecRegistry, bosk)); + 0, codecSupplierHandle(Reference.class, codecRegistry, boskInfo)), + 0, codecSupplierHandle(valueType, codecRegistry, boskInfo)); } }; } @@ -558,7 +558,7 @@ private MethodHandle sideTableWriterHandle(Type valueType, CodecRegistry codecRe /** * @return Map not necessarily in any particular order; caller is expected to apply any desired ordering. */ - private Map gatherParameterValuesByName(Class nodeClass, Map parametersByName, BsonReader reader, DecoderContext decoderContext, CodecRegistry registry, Bosk bosk) { + private Map gatherParameterValuesByName(Class nodeClass, Map parametersByName, BsonReader reader, DecoderContext decoderContext, CodecRegistry registry, BoskInfo boskInfo) { Map parameterValuesByName = new HashMap<>(); while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = reader.readName(); @@ -572,7 +572,7 @@ private Map gatherParameterValuesByNam } Object value; try (@SuppressWarnings("unused") DeserializationScope s = nodeFieldDeserializationScope(nodeClass, fieldName)) { - value = decodeValue(parameter.getParameterizedType(), reader, decoderContext, registry, bosk); + value = decodeValue(parameter.getParameterizedType(), reader, decoderContext, registry, boskInfo); } Object old = parameterValuesByName.put(fieldName, value); if (old != null) { @@ -582,7 +582,7 @@ private Map gatherParameterValuesByNam return parameterValuesByName; } - private Object decodeValue(Type valueType, BsonReader reader, DecoderContext decoderContext, CodecRegistry registry, Bosk bosk) { + private Object decodeValue(Type valueType, BsonReader reader, DecoderContext decoderContext, CodecRegistry registry, BoskInfo boskInfo) { Class valueClass = rawClass(valueType); Object value; if (Phantom.class.isAssignableFrom(valueClass)) { @@ -590,14 +590,14 @@ private Object decodeValue(Type valueType, BsonReader } else if (Optional.class.isAssignableFrom(valueClass)) { // Optional field is present in BSON; wrap it using Optional.of Type contentsType = parameterType(valueType, Optional.class, 0); - value = Optional.of(decodeValue(contentsType, reader, decoderContext, registry, bosk)); + value = Optional.of(decodeValue(contentsType, reader, decoderContext, registry, boskInfo)); } else { - value = getCodec(valueType, valueClass, registry, bosk).decode(reader, decoderContext); + value = getCodec(valueType, valueClass, registry, boskInfo).decode(reader, decoderContext); } return value; } - private MethodHandle computeAllFieldsWriterHandle(Class nodeClass, Map parametersByName, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle computeAllFieldsWriterHandle(Class nodeClass, Map parametersByName, CodecRegistry codecRegistry, BoskInfo boskInfo) { MethodHandle handleUnderConstruction = writeNothingHandle(nodeClass); for (Entry e: parametersByName.entrySet()) { // Here, handleUnderConstruction has args (N,W,E) @@ -609,7 +609,7 @@ private MethodHandle computeA } catch (IllegalAccessException | InvalidTypeException e1) { throw new IllegalStateException("Error in class " + nodeClass.getSimpleName() + ": " + e1.getMessage(), e1); } - MethodHandle fieldWriter = parameterWriterHandle(nodeClass, name, parameter, codecRegistry, bosk); // (P,W,E) + MethodHandle fieldWriter = parameterWriterHandle(nodeClass, name, parameter, codecRegistry, boskInfo); // (P,W,E) MethodHandle writerCall = filterArguments(fieldWriter, 0, getter); // (N,W,E) MethodHandle nestedCall = collectArguments(writerCall, 0, handleUnderConstruction); // (N,W,E,N,W,E) handleUnderConstruction = permuteArguments(nestedCall, writerCall.type(), 0, 1, 2, 0, 1, 2); // (N,W,E) @@ -617,15 +617,15 @@ private MethodHandle computeA return handleUnderConstruction; } - private MethodHandle parameterWriterHandle(Class nodeClass, String name, Parameter parameter, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle parameterWriterHandle(Class nodeClass, String name, Parameter parameter, CodecRegistry codecRegistry, BoskInfo boskInfo) { if (isImplicitParameter(nodeClass, parameter)) { return writeNothingHandle(parameter.getType()); } else { - return valueWriterHandle(name, parameter.getParameterizedType(), codecRegistry, bosk); + return valueWriterHandle(name, parameter.getParameterizedType(), codecRegistry, boskInfo); } } - private MethodHandle valueWriterHandle(String name, Type valueType, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle valueWriterHandle(String name, Type valueType, CodecRegistry codecRegistry, BoskInfo boskInfo) { MethodHandle fieldWriter; Class valueClass = rawClass(valueType); if (Phantom.class.isAssignableFrom(valueClass)) { @@ -633,28 +633,28 @@ private MethodHandle valueWriterHandle(String name, Ty } else if (Optional.class.isAssignableFrom(valueClass)) { // Serialize Optional values only when present Type contentsType = parameterType(valueType, Optional.class, 0); - MethodHandle contentsWriter = valueWriterHandle(name, contentsType, codecRegistry, bosk); + MethodHandle contentsWriter = valueWriterHandle(name, contentsType, codecRegistry, boskInfo); MethodHandle unwrapper = filterArguments(contentsWriter, 0, OPTIONAL_GET.asType(OPTIONAL_GET.type().changeReturnType(rawClass(contentsType)))); fieldWriter = guardWithTest(OPTIONAL_IS_PRESENT, unwrapper, writeNothingHandle(Optional.class)); } else { // Curry in the codec suppliers MethodHandle customized = collectArguments( insertArguments(WRITE_FIELD, 0, name), - 0, codecSupplierHandle(valueType, codecRegistry, bosk)); + 0, codecSupplierHandle(valueType, codecRegistry, boskInfo)); fieldWriter = customized.asType(customized.type().changeParameterType(0, valueClass)); } return fieldWriter; } /** - * We can't call {@link #getCodec(Type, Class, CodecRegistry, Bosk) + * We can't call {@link #getCodec(Type, Class, CodecRegistry, BoskInfo) * getCodec} during the construction of our Codecs, because there * could be cyclic dependencies; we want to defer the call until * run time, at which point all the Codecs will be known already. * * @return {@link MethodHandle} taking no arguments and returning the desired {@link Codec}. */ - private MethodHandle codecSupplierHandle(Type targetType, CodecRegistry codecRegistry, Bosk bosk) { + private MethodHandle codecSupplierHandle(Type targetType, CodecRegistry codecRegistry, BoskInfo boskInfo) { Class targetClass = rawClass(targetType); if (targetClass.getTypeParameters().length >= 1) { if ((targetType instanceof ParameterizedType) || EASYGOING_GENERICS.contains(targetClass)) { @@ -664,7 +664,7 @@ private MethodHandle codecSupplierHandle(Type targetType, CodecRegistry codecReg throw new AssertionError("Class " + targetClass.getSimpleName() + " requires type parameters"); } } - return insertArguments(GET_ANY_CODEC, 0, this, targetType, targetClass, codecRegistry, bosk); + return insertArguments(GET_ANY_CODEC, 0, this, targetType, targetClass, codecRegistry, boskInfo); } /** @@ -736,7 +736,7 @@ private static void writeNothing(Object node, BsonWriter writer, EncoderContext WRITE_CATALOG = LOOKUP.findStatic(BsonPlugin.class, "writeCatalog", methodType(void.class, Codec.class, Catalog.class, BsonWriter.class, EncoderContext.class)); WRITE_SIDE_TABLE = LOOKUP.findStatic(BsonPlugin.class, "writeSideTable", methodType(void.class, Codec.class, Codec.class, SideTable.class, BsonWriter.class, EncoderContext.class)); WRITE_NOTHING = LOOKUP.findStatic(BsonPlugin.class, "writeNothing", methodType(void.class, Object.class, BsonWriter.class, EncoderContext.class)); - GET_ANY_CODEC = LOOKUP.findVirtual(BsonPlugin.class, "getAnyCodec", methodType(Codec.class, Type.class, Class.class, CodecRegistry.class, Bosk.class)); + GET_ANY_CODEC = LOOKUP.findVirtual(BsonPlugin.class, "getAnyCodec", methodType(Codec.class, Type.class, Class.class, CodecRegistry.class, BoskInfo.class)); OPTIONAL_IS_PRESENT = LOOKUP.findVirtual(Optional.class, "isPresent", methodType(boolean.class)); OPTIONAL_GET = LOOKUP.findVirtual(Optional.class, "get", methodType(Object.class)); } catch (NoSuchMethodException | IllegalAccessException e) { diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/Formatter.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/Formatter.java index b1f5062c..73ca6b00 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/Formatter.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/Formatter.java @@ -2,7 +2,7 @@ import com.mongodb.client.model.changestream.ChangeStreamDocument; import com.mongodb.client.model.changestream.UpdateDescription; -import io.vena.bosk.Bosk; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Listing; import io.vena.bosk.MapValue; import io.vena.bosk.Reference; @@ -68,12 +68,12 @@ final class Formatter { */ private volatile MapValue lastEventDiagnosticAttributes = MapValue.empty(); - Formatter(Bosk bosk, BsonPlugin bsonPlugin) { + Formatter(BoskInfo boskInfo, BsonPlugin bsonPlugin) { this.simpleCodecs = CodecRegistries.fromProviders( - bsonPlugin.codecProviderFor(bosk), + bsonPlugin.codecProviderFor(boskInfo), new ValueCodecProvider(), new DocumentCodecProvider()); - this.preferredBoskCodecs = type -> bsonPlugin.getCodec(type, rawClass(type), simpleCodecs, bosk); + this.preferredBoskCodecs = type -> bsonPlugin.getCodec(type, rawClass(type), simpleCodecs, boskInfo); this.deserializationScopeFunction = bsonPlugin::newDeserializationScope; } diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MainDriver.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MainDriver.java index f1ecdefb..db598f1f 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MainDriver.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MainDriver.java @@ -12,6 +12,7 @@ import com.mongodb.client.model.changestream.ChangeStreamDocument; import io.vena.bosk.Bosk; import io.vena.bosk.BoskDriver; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Identifier; import io.vena.bosk.Reference; import io.vena.bosk.StateTreeNode; @@ -55,7 +56,7 @@ * as the database evolves. */ final class MainDriver implements MongoDriver { - private final Bosk bosk; + private final BoskInfo boskInfo; private final ChangeReceiver receiver; private final MongoDriverSettings driverSettings; private final BsonPlugin bsonPlugin; @@ -79,14 +80,14 @@ final class MainDriver implements MongoDriver { private volatile boolean isClosed = false; MainDriver( - Bosk bosk, + BoskInfo boskInfo, MongoClientSettings clientSettings, MongoDriverSettings driverSettings, BsonPlugin bsonPlugin, BoskDriver downstream ) { - try (MDCScope __ = setupMDC(bosk.name(), bosk.instanceID())) { - this.bosk = bosk; + try (MDCScope __ = setupMDC(boskInfo.name(), boskInfo.instanceID())) { + this.boskInfo = boskInfo; this.driverSettings = driverSettings; this.bsonPlugin = bsonPlugin; this.downstream = downstream; @@ -103,10 +104,10 @@ final class MainDriver implements MongoDriver { this.collection = TransactionalCollection.of(rawCollection, mongoClient); LOGGER.debug("Using database \"{}\" collection \"{}\"", driverSettings.database(), COLLECTION_NAME); - Type rootType = bosk.rootReference().targetType(); + Type rootType = boskInfo.rootReference().targetType(); this.listener = new Listener(new FutureTask<>(() -> doInitialRoot(rootType))); - this.formatter = new Formatter(bosk, bsonPlugin); - this.receiver = new ChangeReceiver(bosk.name(), bosk.instanceID(), listener, driverSettings, rawCollection); + this.formatter = new Formatter(boskInfo, bsonPlugin); + this.receiver = new ChangeReceiver(boskInfo.name(), boskInfo.instanceID(), listener, driverSettings, rawCollection); } } @@ -190,7 +191,7 @@ private R doInitialRoot(Type rootType) { root = callDownstreamInitialRoot(rootType); try (var session = collection.newSession()) { FormatDriver preferredDriver = newPreferredFormatDriver(); - preferredDriver.initializeCollection(new StateAndMetadata<>(root, REVISION_ZERO, bosk.diagnosticContext().getAttributes())); + preferredDriver.initializeCollection(new StateAndMetadata<>(root, REVISION_ZERO, boskInfo.rootReference().diagnosticContext().getAttributes())); session.commitTransactionIfAny(); // We can now publish the driver knowing that the transaction, if there is one, has committed publishFormatDriver(preferredDriver); @@ -336,8 +337,7 @@ public void refurbish() throws IOException { @Override public MongoStatus readStatus() throws Exception { try ( - var __1 = bosk.readContext(); - var __2 = collection.newReadOnlySession() + var __ = collection.newReadOnlySession() ) { MongoStatus partialResult = detectFormat().readStatus(); Manifest manifest = loadManifest(); // TODO: Avoid loading the manifest again @@ -401,8 +401,8 @@ public void onConnectionSucceeded() throws // This causes downstream.submitReplacement to be associated with the last update to the state, // which is of dubious relevance. We might just want to use the context from the current thread, // which is probably empty because this runs on the ChangeReceiver thread. - try (var ___ = bosk.rootReference().diagnosticContext().withOnly(loadedState.diagnosticAttributes())) { - downstream.submitReplacement(bosk.rootReference(), loadedState.state()); + try (var ___ = boskInfo.rootReference().diagnosticContext().withOnly(loadedState.diagnosticAttributes())) { + downstream.submitReplacement(boskInfo.rootReference(), loadedState.state()); LOGGER.debug("Done submitting downstream"); } @@ -536,7 +536,7 @@ private Manifest loadManifest() throws UnrecognizedFormatException { private FormatDriver newFormatDriver(long revisionAlreadySeen, DatabaseFormat format) { if (format.equals(SEQUOIA)) { return new SequoiaFormatDriver<>( - bosk, + boskInfo, collection, driverSettings, bsonPlugin, @@ -544,7 +544,7 @@ private FormatDriver newFormatDriver(long revisionAlreadySeen, DatabaseFormat downstream); } else if (format instanceof PandoFormat pandoFormat) { return new PandoFormatDriver<>( - bosk, + boskInfo, collection, driverSettings, pandoFormat, @@ -560,7 +560,7 @@ private MDCScope beginDriverOperation(String description, Object... args) { if (isClosed) { throw new IllegalStateException("Driver is closed"); } - MDCScope ex = setupMDC(bosk.name(), bosk.instanceID()); + MDCScope ex = setupMDC(boskInfo.name(), boskInfo.instanceID()); LOGGER.debug(description, args); if (driverSettings.testing().eventDelayMS() < 0) { LOGGER.debug("| eventDelayMS {}ms ", driverSettings.testing().eventDelayMS()); diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MongoDriver.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MongoDriver.java index 878fb9fd..121e0281 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MongoDriver.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/MongoDriver.java @@ -3,6 +3,7 @@ import com.mongodb.MongoClientSettings; import io.vena.bosk.Bosk; import io.vena.bosk.BoskDriver; +import io.vena.bosk.BoskInfo; import io.vena.bosk.DriverFactory; import io.vena.bosk.StateTreeNode; import io.vena.bosk.drivers.mongo.status.MongoStatus; @@ -49,6 +50,9 @@ public sealed interface MongoDriver */ void refurbish() throws IOException; + /** + * Requires a {@link Bosk.ReadContext}. + */ MongoStatus readStatus() throws Exception; /** @@ -70,6 +74,6 @@ static MongoDriverFactory factory( } interface MongoDriverFactory extends DriverFactory { - @Override MongoDriver build(Bosk bosk, BoskDriver downstream); + @Override MongoDriver build(BoskInfo boskInfo, BoskDriver downstream); } } diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/PandoFormatDriver.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/PandoFormatDriver.java index 05f0ef0a..55cc9110 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/PandoFormatDriver.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/PandoFormatDriver.java @@ -11,8 +11,8 @@ import com.mongodb.client.result.InsertOneResult; import com.mongodb.client.result.UpdateResult; import com.mongodb.lang.Nullable; -import io.vena.bosk.Bosk; import io.vena.bosk.BoskDriver; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Entity; import io.vena.bosk.EnumerableByIdentifier; import io.vena.bosk.Identifier; @@ -86,14 +86,14 @@ final class PandoFormatDriver extends AbstractFormatDri static final BsonString ROOT_DOCUMENT_ID = new BsonString("|"); PandoFormatDriver( - Bosk bosk, + BoskInfo boskInfo, TransactionalCollection collection, MongoDriverSettings driverSettings, PandoFormat format, BsonPlugin bsonPlugin, FlushLock flushLock, BoskDriver downstream ) { - super(bosk.rootReference(), new Formatter(bosk, bsonPlugin)); + super(boskInfo.rootReference(), new Formatter(boskInfo, bsonPlugin)); this.description = getClass().getSimpleName() + ": " + driverSettings; this.settings = driverSettings; this.format = format; diff --git a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/SequoiaFormatDriver.java b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/SequoiaFormatDriver.java index 943d3789..cbb9133f 100644 --- a/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/SequoiaFormatDriver.java +++ b/bosk-mongo/src/main/java/io/vena/bosk/drivers/mongo/SequoiaFormatDriver.java @@ -9,8 +9,8 @@ import com.mongodb.client.model.changestream.UpdateDescription; import com.mongodb.client.result.UpdateResult; import com.mongodb.lang.Nullable; -import io.vena.bosk.Bosk; import io.vena.bosk.BoskDriver; +import io.vena.bosk.BoskInfo; import io.vena.bosk.Identifier; import io.vena.bosk.MapValue; import io.vena.bosk.Reference; @@ -62,14 +62,14 @@ final class SequoiaFormatDriver extends AbstractFormatD static final BsonString DOCUMENT_ID = new BsonString("boskDocument"); SequoiaFormatDriver( - Bosk bosk, + BoskInfo boskInfo, MongoCollection collection, MongoDriverSettings driverSettings, BsonPlugin bsonPlugin, FlushLock flushLock, BoskDriver downstream ) { - super(bosk.rootReference(), new Formatter(bosk, bsonPlugin)); + super(boskInfo.rootReference(), new Formatter(boskInfo, bsonPlugin)); this.description = getClass().getSimpleName() + ": " + driverSettings; this.settings = driverSettings; this.collection = collection; diff --git a/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/AbstractMongoDriverTest.java b/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/AbstractMongoDriverTest.java index f2c7ccd4..d89bfbc7 100644 --- a/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/AbstractMongoDriverTest.java +++ b/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/AbstractMongoDriverTest.java @@ -151,7 +151,7 @@ public TestEntity initialRootWithEmptyCatalog(Bosk testEntityBosk) t } protected DriverFactory createDriverFactory() { - return (bosk, downstream) -> { + return (boskInfo, downstream) -> { MongoDriver driver = MongoDriver.factory( MongoClientSettings.builder(mongoService.clientSettings()) .applyToClusterSettings(builder -> { @@ -164,7 +164,7 @@ protected DriverFactory createDriverFactory() { .build(), driverSettings, new BsonPlugin() - ).build(bosk, downstream); + ).build(boskInfo, downstream); tearDownActions.addFirst(driver::close); return driver; }; diff --git a/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/MongoDriverConformanceTest.java b/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/MongoDriverConformanceTest.java index 04f417b1..c0bc4cd7 100644 --- a/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/MongoDriverConformanceTest.java +++ b/bosk-mongo/src/test/java/io/vena/bosk/drivers/mongo/MongoDriverConformanceTest.java @@ -56,10 +56,10 @@ void runTearDown() { } private DriverFactory createDriverFactory() { - return (bosk, downstream) -> { + return (boskInfo, downstream) -> { MongoDriver driver = MongoDriver.factory( mongoService.clientSettings(), driverSettings, new BsonPlugin() - ).build(bosk, downstream); + ).build(boskInfo, downstream); tearDownActions.addFirst(()->{ driver.close(); mongoService.client() diff --git a/bosk-testing/src/main/java/io/vena/bosk/drivers/AsyncDriver.java b/bosk-testing/src/main/java/io/vena/bosk/drivers/AsyncDriver.java index fc693463..bef48426 100644 --- a/bosk-testing/src/main/java/io/vena/bosk/drivers/AsyncDriver.java +++ b/bosk-testing/src/main/java/io/vena/bosk/drivers/AsyncDriver.java @@ -1,7 +1,7 @@ package io.vena.bosk.drivers; -import io.vena.bosk.Bosk; import io.vena.bosk.BoskDriver; +import io.vena.bosk.BoskInfo; import io.vena.bosk.DriverFactory; import io.vena.bosk.Identifier; import io.vena.bosk.Reference; @@ -20,7 +20,7 @@ @RequiredArgsConstructor(access = PRIVATE) public class AsyncDriver implements BoskDriver { - private final Bosk bosk; + private final BoskInfo bosk; private final BoskDriver downstream; private final ExecutorService executor = Executors.newSingleThreadExecutor(); @@ -68,10 +68,10 @@ public void flush() throws IOException, InterruptedException { private void submitAsyncTask(String description, Runnable task) { LOGGER.debug("Submit {}", description); - var diagnosticAttributes = bosk.diagnosticContext().getAttributes(); + var diagnosticAttributes = bosk.rootReference().diagnosticContext().getAttributes(); executor.submit(()->{ LOGGER.debug("Run {}", description); - try (var __ = bosk.diagnosticContext().withOnly(diagnosticAttributes)) { + try (var __ = bosk.rootReference().diagnosticContext().withOnly(diagnosticAttributes)) { task.run(); } LOGGER.trace("Done {}", description); diff --git a/bosk-testing/src/main/java/io/vena/bosk/drivers/ReportingDriver.java b/bosk-testing/src/main/java/io/vena/bosk/drivers/ReportingDriver.java index a3c44aa9..176dcc7b 100644 --- a/bosk-testing/src/main/java/io/vena/bosk/drivers/ReportingDriver.java +++ b/bosk-testing/src/main/java/io/vena/bosk/drivers/ReportingDriver.java @@ -33,7 +33,7 @@ public class ReportingDriver implements BoskDriver { final Runnable flushListener; public static DriverFactory factory(Consumer listener, Runnable flushListener) { - return (b,d) -> new ReportingDriver<>(d, b.diagnosticContext(), listener, flushListener); + return (b,d) -> new ReportingDriver<>(d, b.rootReference().diagnosticContext(), listener, flushListener); } @Override diff --git a/lib-testing/src/main/java/io/vena/bosk/AbstractRoundTripTest.java b/lib-testing/src/main/java/io/vena/bosk/AbstractRoundTripTest.java index e4430009..2ea18b40 100644 --- a/lib-testing/src/main/java/io/vena/bosk/AbstractRoundTripTest.java +++ b/lib-testing/src/main/java/io/vena/bosk/AbstractRoundTripTest.java @@ -58,9 +58,9 @@ public static DriverFactory directFactory() { } public static DriverFactory factoryThatMakesAReference() { - return (bosk, downstream) -> { - bosk.rootReference(); - return Bosk.simpleDriver(bosk, downstream); + return (boskInfo, downstream) -> { + boskInfo.rootReference(); + return Bosk.simpleDriver(boskInfo, downstream); }; } @@ -73,9 +73,9 @@ private static class JacksonRoundTripDriverFactory implements private final JacksonPlugin jp = new JacksonPlugin(); @Override - public BoskDriver build(Bosk bosk, BoskDriver driver) { - return new PreprocessingDriver(driver) { - final Module module = jp.moduleFor(bosk); + public BoskDriver build(BoskInfo boskInfo, BoskDriver driver) { + return new PreprocessingDriver<>(driver) { + final Module module = jp.moduleFor(boskInfo); final ObjectMapper objectMapper = new ObjectMapper() .registerModule(module) .enable(INDENT_OUTPUT); @@ -112,10 +112,10 @@ public static DriverFactory bsonRoundTripFactory() { @RequiredArgsConstructor private static class BsonRoundTripDriverFactory implements DriverFactory { @Override - public BoskDriver build(Bosk bosk, BoskDriver driver) { + public BoskDriver build(BoskInfo boskInfo, BoskDriver driver) { final BsonPlugin bp = new BsonPlugin(); return new PreprocessingDriver(driver) { - final CodecRegistry codecRegistry = CodecRegistries.fromProviders(bp.codecProviderFor(bosk)); + final CodecRegistry codecRegistry = CodecRegistries.fromProviders(bp.codecProviderFor(boskInfo)); /** * The shortcomings of the Bson library's type system make this @@ -129,7 +129,7 @@ public BoskDriver build(Bosk bosk, BoskDriver driver) { */ @Override T preprocess(Reference reference, T newValue) { - Codec codec = bp.getCodec(reference.targetType(), reference.targetClass(), codecRegistry, bosk); + Codec codec = bp.getCodec(reference.targetType(), reference.targetClass(), codecRegistry, boskInfo); BsonDocument document = new BsonDocument(); try (BsonDocumentWriter writer = new BsonDocumentWriter(document)) { writer.writeStartDocument();