seemingly working record data classes

This commit is contained in:
2024-12-31 18:17:12 +01:00
parent 2a8fbc72de
commit f869178b0f
31 changed files with 62 additions and 1016 deletions

View File

@@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>objects-alloc-deployment</artifactId>
<name>DHFS objects allocation - Deployment</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-common-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.platform.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,6 +0,0 @@
package com.usatiuk.objects.alloc.deployment;
import org.jboss.jandex.DotName;
public record JDataFieldInfo(String name, DotName type) {
}

View File

@@ -1,12 +0,0 @@
package com.usatiuk.objects.alloc.deployment;
import io.quarkus.builder.item.MultiBuildItem;
import org.jboss.jandex.ClassInfo;
public final class JDataIndexBuildItem extends MultiBuildItem {
public final ClassInfo jData;
public JDataIndexBuildItem(ClassInfo jData) {
this.jData = jData;
}
}

View File

@@ -1,16 +0,0 @@
package com.usatiuk.objects.alloc.deployment;
import io.quarkus.builder.item.MultiBuildItem;
import org.jboss.jandex.ClassInfo;
import java.util.Map;
public final class JDataInfoBuildItem extends MultiBuildItem {
public final ClassInfo klass;
public final Map<String, JDataFieldInfo> fields;
public JDataInfoBuildItem(ClassInfo klass, Map<String, JDataFieldInfo> fields) {
this.klass = klass;
this.fields = fields;
}
}

View File

@@ -1,375 +0,0 @@
package com.usatiuk.objects.alloc.deployment;
import com.usatiuk.objects.alloc.runtime.ChangeTrackingJData;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JDataAllocVersionProvider;
import com.usatiuk.objects.common.runtime.JObjectKey;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.ApplicationIndexBuildItem;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.gizmo.*;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.apache.commons.lang3.tuple.Pair;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.reflect.Modifier.*;
class ObjectsAllocProcessor {
@BuildStep
void collectJDatas(BuildProducer<JDataIndexBuildItem> producer, ApplicationIndexBuildItem jandex) {
var jdatas = jandex.getIndex().getAllKnownSubinterfaces(JData.class);
// Collect the leaves
for (var jdata : jdatas) {
System.out.println("Found JData: " + jdata.name());
if (jandex.getIndex().getAllKnownSubinterfaces(jdata.name()).isEmpty()) {
System.out.println("Found JData leaf: " + jdata.name());
producer.produce(new JDataIndexBuildItem(jdata));
}
}
}
private static final String KEY_NAME = "key";
private static final String VERSION_NAME = "version";
private static final List<String> SPECIAL_FIELDS = List.of(KEY_NAME, VERSION_NAME);
String propNameToFieldName(String name) {
return name;
}
String propNameToGetterName(String name) {
return "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
}
String propNameToSetterName(String name) {
return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
}
DotName getDataClassName(ClassInfo jData) {
return DotName.createComponentized(jData.name().packagePrefixName(), jData.name().local() + "Data");
}
DotName getCTClassName(ClassInfo jData) {
return DotName.createComponentized(jData.name().packagePrefixName(), jData.name().local() + "CTData");
}
DotName getImmutableClassName(ClassInfo jData) {
return DotName.createComponentized(jData.name().packagePrefixName(), jData.name().local() + "ImmutableData");
}
@BuildStep
void generateDataClass(List<JDataInfoBuildItem> jDataItems, BuildProducer<GeneratedClassBuildItem> generatedClasses) {
var gizmoAdapter = new GeneratedClassGizmoAdaptor(generatedClasses, true);
for (var item : jDataItems) {
try (ClassCreator classCreator = ClassCreator.builder()
.className(getDataClassName(item.klass).toString())
.interfaces(JData.class)
.interfaces(item.klass.name().toString())
.interfaces(Serializable.class)
.classOutput(gizmoAdapter)
.build()) {
var fieldsMap = createFields(item, classCreator);
for (var field : fieldsMap.values()) {
if (!SPECIAL_FIELDS.contains(field.getName())) {
try (var setter = classCreator.getMethodCreator(propNameToSetterName(field.getName()), void.class, field.getType())) {
setter.writeInstanceField(field, setter.getThis(), setter.getMethodParam(0));
setter.returnVoid();
}
}
}
try (var constructor = classCreator.getConstructorCreator(JObjectKey.class, long.class)) {
constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(Object.class), constructor.getThis());
constructor.writeInstanceField(fieldsMap.get(KEY_NAME), constructor.getThis(), constructor.getMethodParam(0));
constructor.writeInstanceField(fieldsMap.get(VERSION_NAME), constructor.getThis(), constructor.getMethodParam(1));
constructor.returnVoid();
}
}
}
}
private static final String MODIFIED_FIELD_NAME = "_modified";
private static final String ON_CHANGE_METHOD_NAME = "onChange";
@BuildStep
void generateCTClass(List<JDataInfoBuildItem> jDataItems, BuildProducer<GeneratedClassBuildItem> generatedClasses) {
var gizmoAdapter = new GeneratedClassGizmoAdaptor(generatedClasses, true);
for (var item : jDataItems) {
try (ClassCreator classCreator = ClassCreator.builder()
.className(getCTClassName(item.klass).toString())
.interfaces(JData.class, ChangeTrackingJData.class)
.interfaces(item.klass.name().toString())
.interfaces(Serializable.class)
.classOutput(gizmoAdapter)
.build()) {
var modified = classCreator.getFieldCreator(MODIFIED_FIELD_NAME, boolean.class);
modified.setModifiers(PRIVATE | TRANSIENT);
try (var modifiedGetter = classCreator.getMethodCreator("isModified", boolean.class)) {
modifiedGetter.returnValue(modifiedGetter.readInstanceField(modified.getFieldDescriptor(), modifiedGetter.getThis()));
}
try (var onChanged = classCreator.getMethodCreator(ON_CHANGE_METHOD_NAME, void.class)) {
onChanged.writeInstanceField(modified.getFieldDescriptor(), onChanged.getThis(), onChanged.load(true));
onChanged.returnVoid();
}
try (var wrapped = classCreator.getMethodCreator("wrapped", item.klass.name().toString())) {
wrapped.returnValue(wrapped.getThis());
}
try (var wrapped = classCreator.getMethodCreator("wrapped", JData.class)) {
wrapped.returnValue(wrapped.getThis());
}
var fieldsMap = createFields(item, classCreator);
for (var field : fieldsMap.values()) {
if (!SPECIAL_FIELDS.contains(field.getName())) {
try (var setter = classCreator.getMethodCreator(propNameToSetterName(field.getName()), void.class, field.getType())) {
setter.writeInstanceField(field, setter.getThis(), setter.getMethodParam(0));
setter.invokeVirtualMethod(MethodDescriptor.ofMethod(classCreator.getClassName(), ON_CHANGE_METHOD_NAME, void.class), setter.getThis());
setter.returnVoid();
}
}
}
try (var constructor = classCreator.getConstructorCreator(item.klass.name().toString(), long.class.getName())) {
constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(Object.class), constructor.getThis());
constructor.writeInstanceField(modified.getFieldDescriptor(), constructor.getThis(), constructor.load(true)); // FIXME:
for (var field : fieldsMap.values()) {
if (!Objects.equals(field.getName(), VERSION_NAME))
constructor.writeInstanceField(field, constructor.getThis(), constructor.invokeInterfaceMethod(
MethodDescriptor.ofMethod(item.klass.name().toString(), propNameToGetterName(field.getName()), field.getType()),
constructor.getMethodParam(0)
));
}
constructor.writeInstanceField(fieldsMap.get(VERSION_NAME), constructor.getThis(), constructor.getMethodParam(1));
constructor.returnVoid();
}
}
}
}
@BuildStep
void generateImmutableClass(List<JDataInfoBuildItem> jDataItems, BuildProducer<GeneratedClassBuildItem> generatedClasses) {
var gizmoAdapter = new GeneratedClassGizmoAdaptor(generatedClasses, true);
for (var item : jDataItems) {
try (ClassCreator classCreator = ClassCreator.builder()
.className(getImmutableClassName(item.klass).toString())
.interfaces(JData.class, ChangeTrackingJData.class)
.interfaces(item.klass.name().toString())
.interfaces(Serializable.class)
.classOutput(gizmoAdapter)
.build()) {
var fieldsMap = createFields(item, classCreator);
for (var field : fieldsMap.values()) {
try (var setter = classCreator.getMethodCreator(propNameToSetterName(field.getName()), void.class, field.getType())) {
setter.throwException(UnsupportedOperationException.class, "Immutable object");
}
}
try (var constructor = classCreator.getConstructorCreator(item.klass.name().toString())) {
constructor.invokeSpecialMethod(MethodDescriptor.ofConstructor(Object.class), constructor.getThis());
for (var field : fieldsMap.values()) {
constructor.writeInstanceField(field, constructor.getThis(), constructor.invokeInterfaceMethod(
MethodDescriptor.ofMethod(item.klass.name().toString(), propNameToGetterName(field.getName()), field.getType()),
constructor.getMethodParam(0)
));
}
constructor.returnVoid();
}
}
}
}
private Map<String, FieldDescriptor> createFields(JDataInfoBuildItem item, ClassCreator classCreator) {
return item.fields.values().stream().map(jDataFieldInfo -> {
var fc = classCreator.getFieldCreator(propNameToFieldName(jDataFieldInfo.name()), jDataFieldInfo.type().toString());
if (SPECIAL_FIELDS.contains(jDataFieldInfo.name())) {
fc.setModifiers(PRIVATE | FINAL);
} else {
fc.setModifiers(PRIVATE);
}
try (var getter = classCreator.getMethodCreator(propNameToGetterName(jDataFieldInfo.name()), jDataFieldInfo.type().toString())) {
getter.returnValue(getter.readInstanceField(fc.getFieldDescriptor(), getter.getThis()));
}
return Pair.of(jDataFieldInfo, fc.getFieldDescriptor());
}).collect(Collectors.toUnmodifiableMap(i -> i.getLeft().name(), Pair::getRight));
}
List<ClassInfo> collectInterfaces(ClassInfo type, ApplicationIndexBuildItem jandex) {
return Stream.concat(Stream.of(type), type.interfaceNames().stream()
.flatMap(x -> {
var ret = jandex.getIndex().getClassByName(x);
if (ret == null) {
System.out.println("Interface not found! " + x);
return Stream.empty();
}
return Stream.of(ret);
})
.flatMap(i -> collectInterfaces(i, jandex).stream()))
.collect(Collectors.toList());
}
Map<String, MethodInfo> collectMethods(List<ClassInfo> types) {
return types.stream()
.flatMap(x -> x.methods().stream())
.filter(x -> x.name().startsWith("get") || x.name().startsWith("set"))
.collect(Collectors.toMap(MethodInfo::name, x -> x));
}
@BuildStep
void collectData(BuildProducer<JDataInfoBuildItem> producer, List<JDataIndexBuildItem> items, ApplicationIndexBuildItem jandex) {
for (var item : items) {
var methodNameToInfo = collectMethods(collectInterfaces(item.jData, jandex));
var reducableSet = new TreeSet<>(methodNameToInfo.keySet());
var fields = new TreeMap<String, JDataFieldInfo>();
if (reducableSet.contains(propNameToGetterName(KEY_NAME))) {
reducableSet.remove(propNameToGetterName(KEY_NAME));
var methodInfo = methodNameToInfo.get(propNameToGetterName(KEY_NAME));
if (!methodInfo.returnType().name().equals(DotName.createSimple(JObjectKey.class.getName()))) {
throw new RuntimeException("Key getter must return JObjectKey");
}
fields.put(KEY_NAME, new JDataFieldInfo(KEY_NAME, methodNameToInfo.get(propNameToGetterName(KEY_NAME)).returnType().name()));
} else {
// throw new RuntimeException("Missing key getter");
System.out.println("Missing key getter for " + item.jData);
// FIXME!: No matter what, I couldn't get JData to get indexed by jandex
fields.put(KEY_NAME, new JDataFieldInfo(KEY_NAME, DotName.createSimple(JObjectKey.class)));
fields.put(VERSION_NAME, new JDataFieldInfo(VERSION_NAME, DotName.createSimple(long.class)));
}
// Find pairs of getters and setters
// FIXME:
while (!reducableSet.isEmpty()) {
var name = reducableSet.first();
reducableSet.remove(name);
if (name.startsWith("get")) {
var setterName = "set" + name.substring(3);
if (reducableSet.contains(setterName)) {
reducableSet.remove(setterName);
} else {
throw new RuntimeException("Missing setter for getter: " + name);
}
var getter = methodNameToInfo.get(name);
var setter = methodNameToInfo.get(setterName);
if (!getter.returnType().equals(setter.parameters().getFirst().type())) {
throw new RuntimeException("Getter and setter types do not match: " + name);
}
var variableName = name.substring(3, 4).toLowerCase() + name.substring(4);
fields.put(variableName, new JDataFieldInfo(variableName, getter.returnType().name()));
} else {
throw new RuntimeException("Unknown method name: " + name);
}
}
producer.produce(new JDataInfoBuildItem(item.jData, Collections.unmodifiableMap(fields)));
}
}
// Returns false branch
void matchClass(BytecodeCreator bytecodeCreator, ResultHandle toMatch, List<ClassInfo> types, ClassTagFunction fn) {
for (var type : types) {
var eq = bytecodeCreator.instanceOf(toMatch, type.name().toString());
var cmp = bytecodeCreator.ifTrue(eq);
fn.apply(type, cmp.trueBranch(), toMatch);
}
}
interface ClassTagFunction {
void apply(ClassInfo type, BytecodeCreator branch, ResultHandle value);
}
// Returns false branch
void matchClassTag(BytecodeCreator bytecodeCreator, ResultHandle toMatch, List<ClassInfo> types, ClassTagFunction fn) {
for (var type : types) {
var eq = bytecodeCreator.invokeVirtualMethod(
MethodDescriptor.ofMethod(Object.class, "equals", boolean.class, Object.class),
toMatch,
bytecodeCreator.loadClass(type.name().toString())
);
var cmp = bytecodeCreator.ifTrue(eq);
fn.apply(type, cmp.trueBranch(), toMatch);
}
}
@BuildStep
void makeJDataThingy(List<JDataInfoBuildItem> jDataItems,
BuildProducer<GeneratedBeanBuildItem> generatedBeans) {
var data = jDataItems.stream().collect(Collectors.toUnmodifiableMap(i -> i.klass, x -> x));
var classes = data.keySet().stream().map(ClassInfo::asClass).toList();
var gizmoAdapter = new GeneratedBeanGizmoAdaptor(generatedBeans);
try (ClassCreator classCreator = ClassCreator.builder()
.className("com.usatiuk.objects.alloc.generated.ObjectAllocatorImpl")
.interfaces(ObjectAllocator.class)
.classOutput(gizmoAdapter)
.build()) {
classCreator.addAnnotation(Singleton.class);
var versionProvider = classCreator.getFieldCreator("versionProvider", JDataAllocVersionProvider.class);
versionProvider.addAnnotation(Inject.class);
versionProvider.setModifiers(PUBLIC);
Function<BytecodeCreator, ResultHandle> loadVersion = (block) -> block.invokeInterfaceMethod(
MethodDescriptor.ofMethod(JDataAllocVersionProvider.class, "getVersion", long.class),
block.readInstanceField(versionProvider.getFieldDescriptor(), block.getThis())
);
try (MethodCreator methodCreator = classCreator.getMethodCreator("create", JData.class, Class.class, JObjectKey.class)) {
matchClassTag(methodCreator, methodCreator.getMethodParam(0), classes, (type, branch, value) -> {
branch.returnValue(branch.newInstance(MethodDescriptor.ofConstructor(getDataClassName(type).toString(), JObjectKey.class, long.class), branch.getMethodParam(1), loadVersion.apply(branch)));
});
methodCreator.throwException(IllegalArgumentException.class, "Unknown type");
}
try (MethodCreator methodCreator = classCreator.getMethodCreator("copy", ChangeTrackingJData.class, JData.class)) {
matchClass(methodCreator, methodCreator.getMethodParam(0), classes, (type, branch, value) -> {
branch.returnValue(branch.newInstance(MethodDescriptor.ofConstructor(getCTClassName(type).toString(), type.name().toString(), long.class.getName()), value, loadVersion.apply(branch)));
});
methodCreator.throwException(IllegalArgumentException.class, "Unknown type");
}
try (MethodCreator methodCreator = classCreator.getMethodCreator("unmodifiable", JData.class, JData.class)) {
matchClass(methodCreator, methodCreator.getMethodParam(0), classes, (type, branch, value) -> {
branch.returnValue(branch.newInstance(MethodDescriptor.ofConstructor(getImmutableClassName(type).toString(), type.name().toString()), value));
});
methodCreator.throwException(IllegalArgumentException.class, "Unknown type");
}
}
}
}

View File

@@ -1,25 +0,0 @@
package com.usatiuk.objects.alloc.test;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import io.quarkus.test.QuarkusDevModeTest;
@Disabled
public class ObjectsAllocDevModeTest {
// Start hot reload (DevMode) test with your extension loaded
@RegisterExtension
static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
@Test
public void writeYourOwnDevModeTest() {
// Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information
Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName());
}
}

View File

@@ -1,26 +0,0 @@
package com.usatiuk.objects.alloc.test;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import io.quarkus.test.QuarkusUnitTest;
import jakarta.inject.Inject;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@Disabled
public class ObjectsAllocTest {
// Start unit test with your extension loaded
@RegisterExtension
static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class));
@Test
public void writeYourOwnUnitTest() {
// Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information
Assertions.assertTrue(true, "Add some assertions to " + getClass().getName());
}
}

View File

@@ -1,107 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>objects-alloc-integration-tests</artifactId>
<name>DHFS objects allocation - Integration Tests</name>
<properties>
<skipITs>true</skipITs>
</properties>
<dependencies>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-common-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${native.surefire.skip}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled>
</properties>
</profile>
</profiles>
</project>

View File

@@ -1,20 +0,0 @@
package com.usatiuk.objects.alloc.it;
import com.usatiuk.objects.common.runtime.JDataAllocVersionProvider;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class DummyVersionProvider implements JDataAllocVersionProvider {
long version = 0;
@Override
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
}

View File

@@ -1,18 +0,0 @@
package com.usatiuk.objects.alloc.it;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
public interface TestJDataAssorted extends JData {
String getLastName();
void setLastName(String lastName);
long getAge();
void setAge(long age);
JObjectKey getKidKey();
void setKidKey(JObjectKey kid);
}

View File

@@ -1,6 +0,0 @@
package com.usatiuk.objects.alloc.it;
import com.usatiuk.objects.common.runtime.JData;
public interface TestJDataEmpty extends JData {
}

View File

@@ -1 +0,0 @@
quarkus.package.jar.decompiler.enabled=true

View File

@@ -1,96 +0,0 @@
package com.usatiuk.objects.alloc.it;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JObjectKey;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@QuarkusTest
public class ObjectAllocIT {
@Inject
ObjectAllocator objectAllocator;
@Inject
DummyVersionProvider dummyVersionProvider;
@Test
void testCreateVersion() {
dummyVersionProvider.setVersion(1);
var newObject = objectAllocator.create(TestJDataEmpty.class, new JObjectKey("TestJDataEmptyKey"));
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataEmptyKey", newObject.getKey().name());
Assertions.assertEquals(1, newObject.getVersion());
}
@Test
void testCopyVersion() {
dummyVersionProvider.setVersion(1);
var newObject = objectAllocator.create(TestJDataAssorted.class, new JObjectKey("TestJDataAssorted"));
newObject.setLastName("1");
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataAssorted", newObject.getKey().name());
Assertions.assertEquals(1, newObject.getVersion());
dummyVersionProvider.setVersion(2);
var copyObject = objectAllocator.copy(newObject);
Assertions.assertNotNull(copyObject);
Assertions.assertFalse(copyObject.isModified());
Assertions.assertEquals("1", copyObject.wrapped().getLastName());
Assertions.assertEquals(2, copyObject.wrapped().getVersion());
Assertions.assertEquals(1, newObject.getVersion());
copyObject.wrapped().setLastName("2");
Assertions.assertTrue(copyObject.isModified());
Assertions.assertEquals("2", copyObject.wrapped().getLastName());
Assertions.assertEquals("1", newObject.getLastName());
}
@Test
void testCreateObject() {
var newObject = objectAllocator.create(TestJDataEmpty.class, new JObjectKey("TestJDataEmptyKey"));
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataEmptyKey", newObject.getKey().name());
}
@Test
void testCopyObject() {
var newObject = objectAllocator.create(TestJDataAssorted.class, new JObjectKey("TestJDataAssorted"));
newObject.setLastName("1");
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataAssorted", newObject.getKey().name());
var copyObject = objectAllocator.copy(newObject);
Assertions.assertNotNull(copyObject);
Assertions.assertFalse(copyObject.isModified());
Assertions.assertEquals("1", copyObject.wrapped().getLastName());
copyObject.wrapped().setLastName("2");
Assertions.assertTrue(copyObject.isModified());
Assertions.assertEquals("2", copyObject.wrapped().getLastName());
Assertions.assertEquals("1", newObject.getLastName());
}
@Test
void testImmutable() {
var newObject = objectAllocator.create(TestJDataAssorted.class, new JObjectKey("TestJDataAssorted"));
newObject.setLastName("1");
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataAssorted", newObject.getKey().name());
var copyObject = objectAllocator.unmodifiable(newObject);
Assertions.assertNotNull(copyObject);
Assertions.assertEquals("1", copyObject.getLastName());
Assertions.assertThrows(UnsupportedOperationException.class, () -> copyObject.setLastName("2"));
}
@Test
void testImmutable2() {
var newObject = objectAllocator.create(TestJDataEmpty.class, new JObjectKey("TestJDataEmpty"));
Assertions.assertNotNull(newObject);
Assertions.assertEquals("TestJDataEmpty", newObject.getKey().name());
var copyObject = objectAllocator.unmodifiable(newObject);
Assertions.assertNotNull(copyObject);
}
}

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.usatiuk.dhfs</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>DHFS objects allocation - Parent</name>
<modules>
<module>deployment</module>
<module>runtime</module>
<module>integration-tests</module>
</modules>
</project>

View File

@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>objects-alloc</artifactId>
<name>DHFS objects allocation - Runtime</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>extension-descriptor</goal>
</goals>
<configuration>
<deployment>${project.groupId}:${project.artifactId}-deployment:${project.version}
</deployment>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.platform.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,9 +0,0 @@
package com.usatiuk.objects.alloc.runtime;
import com.usatiuk.objects.common.runtime.JData;
public interface ChangeTrackingJData<T extends JData> {
T wrapped();
boolean isModified();
}

View File

@@ -1,12 +0,0 @@
package com.usatiuk.objects.alloc.runtime;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
public interface ObjectAllocator {
<T extends JData> T create(Class<T> type, JObjectKey key);
<T extends JData> ChangeTrackingJData<T> copy(T obj);
<T extends JData> T unmodifiable(T obj);
}

View File

@@ -1,9 +0,0 @@
name: DHFS objects allocation
#description: Do something useful.
metadata:
# keywords:
# - objects-alloc
# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension
# categories:
# - "miscellaneous"
# status: "preview"

View File

@@ -1,12 +1,12 @@
package com.usatiuk.objects.common.runtime;
import java.io.Serializable;
// TODO: This could be maybe moved to a separate module?
// The base class for JObject data
// Only one instance of this "exists" per key, the instance in the manager is canonical
// When committing a transaction, the instance is checked against it, if it isn't the same, a race occurred.
// It is immutable, its version is filled in by the allocator from the AllocVersionProvider
public interface JData {
JObjectKey getKey();
long getVersion();
public interface JData extends Serializable {
JObjectKey key();
}

View File

@@ -1,5 +0,0 @@
package com.usatiuk.objects.common.runtime;
public interface JDataAllocVersionProvider {
long getVersion();
}

View File

@@ -3,4 +3,7 @@ package com.usatiuk.objects.common.runtime;
import java.io.Serializable;
public record JObjectKey(String name) implements Serializable {
public static JObjectKey of(String name) {
return new JObjectKey(name);
}
}

View File

@@ -64,17 +64,6 @@
<artifactId>supportlib</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-alloc-deployment</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.usatiuk</groupId>
<artifactId>objects-common</artifactId>

View File

@@ -5,7 +5,6 @@ import com.usatiuk.dhfs.objects.persistence.TxManifest;
import com.usatiuk.dhfs.objects.transaction.*;
import com.usatiuk.dhfs.utils.AutoCloseableNoThrow;
import com.usatiuk.dhfs.utils.DataLocker;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
import io.quarkus.logging.Log;
@@ -33,8 +32,6 @@ public class JObjectManager {
@Inject
ObjectSerializer<JDataVersionedWrapper> objectSerializer;
@Inject
ObjectAllocator objectAllocator;
@Inject
TransactionFactory transactionFactory;
private final List<PreCommitTxHook> _preCommitTxHooks;
@@ -52,7 +49,7 @@ public class JObjectManager {
public JDataWrapper(JDataVersionedWrapper<T> referent) {
super(referent);
var key = referent.data().getKey();
var key = referent.data().key();
CLEANER.register(referent, () -> {
_objects.remove(key, this);
});

View File

@@ -1,7 +1,6 @@
package com.usatiuk.dhfs.objects.transaction;
import com.usatiuk.dhfs.objects.JDataVersionedWrapper;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
import jakarta.enterprise.context.ApplicationScoped;
@@ -16,9 +15,6 @@ import java.util.Optional;
@ApplicationScoped
public class TransactionFactoryImpl implements TransactionFactory {
@Inject
ObjectAllocator objectAllocator;
private class TransactionImpl implements TransactionPrivate {
@Getter(AccessLevel.PUBLIC)
private final long _id;
@@ -68,8 +64,8 @@ public class TransactionFactoryImpl implements TransactionFactory {
public void put(JData obj) {
// get(JData.class, obj.getKey(), LockingStrategy.OPTIMISTIC);
_writes.put(obj.getKey(), new TxRecord.TxObjectRecordWrite<>(obj));
_newWrites.put(obj.getKey(), new TxRecord.TxObjectRecordWrite<>(obj));
_writes.put(obj.key(), new TxRecord.TxObjectRecordWrite<>(obj));
_newWrites.put(obj.key(), new TxRecord.TxObjectRecordWrite<>(obj));
}
@Override

View File

@@ -1,16 +0,0 @@
package com.usatiuk.dhfs.objects.transaction;
import com.usatiuk.objects.common.runtime.JDataAllocVersionProvider;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@Singleton
public class TransactionObjectAllocVersionProvider implements JDataAllocVersionProvider {
@Inject
Transaction transaction;
public long getVersion() {
return transaction.getId();
}
}

View File

@@ -11,7 +11,7 @@ public class TxRecord {
public record TxObjectRecordWrite<T extends JData>(JData data) implements TxObjectRecord<T> {
@Override
public JObjectKey key() {
return data.getKey();
return data.key();
}
}

View File

@@ -3,7 +3,6 @@ package com.usatiuk.dhfs.objects;
import com.usatiuk.dhfs.objects.data.Parent;
import com.usatiuk.dhfs.objects.transaction.LockingStrategy;
import com.usatiuk.dhfs.objects.transaction.Transaction;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JObjectKey;
import io.quarkus.logging.Log;
import io.quarkus.test.junit.QuarkusTest;
@@ -25,23 +24,19 @@ public class ObjectsTest {
@Inject
Transaction curTx;
@Inject
ObjectAllocator alloc;
@Test
void createObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("Parent"), "John");
curTx.put(newParent);
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent")).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
var parent = curTx.get(Parent.class, JObjectKey.of("Parent")).orElse(null);
Assertions.assertEquals("John", parent.name());
txm.commit();
}
}
@@ -50,28 +45,27 @@ public class ObjectsTest {
void createDeleteObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("Parent2"), "John");
curTx.put(newParent);
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent")).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
var parent = curTx.get(Parent.class, JObjectKey.of("Parent2")).orElse(null);
Assertions.assertEquals("John", parent.name());
txm.commit();
}
{
txm.begin();
curTx.delete(new JObjectKey("Parent"));
curTx.delete(new JObjectKey("Parent2"));
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent")).orElse(null);
var parent = curTx.get(Parent.class, new JObjectKey("Parent2")).orElse(null);
Assertions.assertNull(parent);
txm.commit();
}
@@ -81,22 +75,20 @@ public class ObjectsTest {
void createCreateObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent7"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("Parent7"), "John");
curTx.put(newParent);
txm.commit();
}
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent7"));
newParent.setLastName("John2");
var newParent = new Parent(JObjectKey.of("Parent7"), "John2");
curTx.put(newParent);
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent7")).orElse(null);
Assertions.assertEquals("John2", parent.getLastName());
Assertions.assertEquals("John2", parent.name());
txm.commit();
}
}
@@ -105,8 +97,7 @@ public class ObjectsTest {
void editObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent3"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("Parent3"), "John");
curTx.put(newParent);
txm.commit();
}
@@ -114,23 +105,23 @@ public class ObjectsTest {
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent3"), LockingStrategy.OPTIMISTIC).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
parent.setLastName("John2");
Assertions.assertEquals("John", parent.name());
curTx.put(parent.toBuilder().name("John2").build());
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent3"), LockingStrategy.WRITE).orElse(null);
Assertions.assertEquals("John2", parent.getLastName());
parent.setLastName("John3");
Assertions.assertEquals("John2", parent.name());
curTx.put(parent.toBuilder().name("John3").build());
txm.commit();
}
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("Parent3")).orElse(null);
Assertions.assertEquals("John3", parent.getLastName());
Assertions.assertEquals("John3", parent.name());
txm.commit();
}
}
@@ -148,8 +139,7 @@ public class ObjectsTest {
Log.warn("Thread 1");
txm.begin();
barrier.await();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent2"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("Parent2"), "John");
curTx.put(newParent);
Log.warn("Thread 1 commit");
txm.commit();
@@ -164,8 +154,7 @@ public class ObjectsTest {
Log.warn("Thread 2");
txm.begin();
barrier.await();
var newParent = alloc.create(Parent.class, new JObjectKey("Parent2"));
newParent.setLastName("John2");
var newParent = new Parent(JObjectKey.of("Parent2"), "John2");
curTx.put(newParent);
Log.warn("Thread 2 commit");
txm.commit();
@@ -184,9 +173,9 @@ public class ObjectsTest {
if (!thread1Failed.get()) {
Assertions.assertTrue(thread2Failed.get());
Assertions.assertEquals("John", got.getLastName());
Assertions.assertEquals("John", got.name());
} else if (!thread2Failed.get()) {
Assertions.assertEquals("John2", got.getLastName());
Assertions.assertEquals("John2", got.name());
} else {
Assertions.fail("No thread succeeded");
}
@@ -198,8 +187,7 @@ public class ObjectsTest {
String key = "Parent4" + strategy.name();
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey(key));
newParent.setLastName("John3");
var newParent = new Parent(JObjectKey.of(key), "John3");
curTx.put(newParent);
txm.commit();
}
@@ -216,7 +204,7 @@ public class ObjectsTest {
txm.begin();
barrier.await();
var parent = curTx.get(Parent.class, new JObjectKey(key), strategy).orElse(null);
parent.setLastName("John");
curTx.put(parent.toBuilder().name("John").build());
Log.warn("Thread 1 commit");
txm.commit();
thread1Failed.set(false);
@@ -231,7 +219,7 @@ public class ObjectsTest {
txm.begin();
barrier.await();
var parent = curTx.get(Parent.class, new JObjectKey(key), strategy).orElse(null);
parent.setLastName("John2");
curTx.put(parent.toBuilder().name("John2").build());
Log.warn("Thread 2 commit");
txm.commit();
thread2Failed.set(false);
@@ -249,9 +237,9 @@ public class ObjectsTest {
if (!thread1Failed.get()) {
Assertions.assertTrue(thread2Failed.get());
Assertions.assertEquals("John", got.getLastName());
Assertions.assertEquals("John", got.name());
} else if (!thread2Failed.get()) {
Assertions.assertEquals("John2", got.getLastName());
Assertions.assertEquals("John2", got.name());
} else {
Assertions.fail("No thread succeeded");
}

View File

@@ -2,7 +2,6 @@ package com.usatiuk.dhfs.objects;
import com.usatiuk.dhfs.objects.data.Parent;
import com.usatiuk.dhfs.objects.transaction.Transaction;
import com.usatiuk.objects.alloc.runtime.ObjectAllocator;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
import io.quarkus.test.junit.QuarkusTest;
@@ -22,9 +21,6 @@ public class PreCommitTxHookTest {
@Inject
Transaction curTx;
@Inject
ObjectAllocator alloc;
@ApplicationScoped
public static class DummyPreCommitTxHook implements PreCommitTxHook {
}
@@ -36,8 +32,8 @@ public class PreCommitTxHookTest {
void createObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("ParentCreate"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("ParentCreate"), "John");
curTx.put(newParent);
curTx.put(newParent);
txm.commit();
}
@@ -45,14 +41,14 @@ public class PreCommitTxHookTest {
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("ParentCreate")).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
Assertions.assertEquals("John", parent.name());
txm.commit();
}
ArgumentCaptor<JData> dataCaptor = ArgumentCaptor.forClass(JData.class);
ArgumentCaptor<JObjectKey> keyCaptor = ArgumentCaptor.forClass(JObjectKey.class);
Mockito.verify(spyHook, Mockito.times(1)).onCreate(keyCaptor.capture(), dataCaptor.capture());
Assertions.assertEquals("John", ((Parent) dataCaptor.getValue()).getLastName());
Assertions.assertEquals("John", ((Parent) dataCaptor.getValue()).name());
Assertions.assertEquals(new JObjectKey("ParentCreate"), keyCaptor.getValue());
}
@@ -60,8 +56,7 @@ public class PreCommitTxHookTest {
void deleteObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("ParentDel"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("ParentDel"), "John");
curTx.put(newParent);
txm.commit();
}
@@ -69,7 +64,7 @@ public class PreCommitTxHookTest {
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("ParentDel")).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
Assertions.assertEquals("John", parent.name());
txm.commit();
}
@@ -82,7 +77,7 @@ public class PreCommitTxHookTest {
ArgumentCaptor<JData> dataCaptor = ArgumentCaptor.forClass(JData.class);
ArgumentCaptor<JObjectKey> keyCaptor = ArgumentCaptor.forClass(JObjectKey.class);
Mockito.verify(spyHook, Mockito.times(1)).onDelete(keyCaptor.capture(), dataCaptor.capture());
Assertions.assertEquals("John", ((Parent) dataCaptor.getValue()).getLastName());
Assertions.assertEquals("John", ((Parent) dataCaptor.getValue()).name());
Assertions.assertEquals(new JObjectKey("ParentDel"), keyCaptor.getValue());
}
@@ -90,16 +85,14 @@ public class PreCommitTxHookTest {
void editObject() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("ParentEdit"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("ParentEdit"), "John");
curTx.put(newParent);
txm.commit();
}
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("ParentEdit"));
newParent.setLastName("John changed");
var newParent = new Parent(JObjectKey.of("ParentEdit"), "John changed");
curTx.put(newParent);
txm.commit();
}
@@ -108,8 +101,8 @@ public class PreCommitTxHookTest {
ArgumentCaptor<JData> dataCaptorNew = ArgumentCaptor.forClass(JData.class);
ArgumentCaptor<JObjectKey> keyCaptor = ArgumentCaptor.forClass(JObjectKey.class);
Mockito.verify(spyHook, Mockito.times(1)).onChange(keyCaptor.capture(), dataCaptorOld.capture(), dataCaptorNew.capture());
Assertions.assertEquals("John", ((Parent) dataCaptorOld.getValue()).getLastName());
Assertions.assertEquals("John changed", ((Parent) dataCaptorNew.getValue()).getLastName());
Assertions.assertEquals("John", ((Parent) dataCaptorOld.getValue()).name());
Assertions.assertEquals("John changed", ((Parent) dataCaptorNew.getValue()).name());
Assertions.assertEquals(new JObjectKey("ParentEdit"), keyCaptor.getValue());
}
@@ -117,8 +110,7 @@ public class PreCommitTxHookTest {
void editObjectWithGet() {
{
txm.begin();
var newParent = alloc.create(Parent.class, new JObjectKey("ParentEdit2"));
newParent.setLastName("John");
var newParent = new Parent(JObjectKey.of("ParentEdit2"), "John");
curTx.put(newParent);
txm.commit();
}
@@ -126,10 +118,8 @@ public class PreCommitTxHookTest {
{
txm.begin();
var parent = curTx.get(Parent.class, new JObjectKey("ParentEdit2")).orElse(null);
Assertions.assertEquals("John", parent.getLastName());
var newParent = alloc.create(Parent.class, new JObjectKey("ParentEdit2"));
newParent.setLastName("John changed");
curTx.put(newParent);
Assertions.assertEquals("John", parent.name());
curTx.put(parent.toBuilder().name("John changed").build());
txm.commit();
}
@@ -137,8 +127,8 @@ public class PreCommitTxHookTest {
ArgumentCaptor<JData> dataCaptorNew = ArgumentCaptor.forClass(JData.class);
ArgumentCaptor<JObjectKey> keyCaptor = ArgumentCaptor.forClass(JObjectKey.class);
Mockito.verify(spyHook, Mockito.times(1)).onChange(keyCaptor.capture(), dataCaptorOld.capture(), dataCaptorNew.capture());
Assertions.assertEquals("John", ((Parent) dataCaptorOld.getValue()).getLastName());
Assertions.assertEquals("John changed", ((Parent) dataCaptorNew.getValue()).getLastName());
Assertions.assertEquals("John", ((Parent) dataCaptorOld.getValue()).name());
Assertions.assertEquals("John changed", ((Parent) dataCaptorNew.getValue()).name());
Assertions.assertEquals(new JObjectKey("ParentEdit2"), keyCaptor.getValue());
}

View File

@@ -1,9 +1,9 @@
package com.usatiuk.dhfs.objects.data;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
import lombok.Builder;
public interface Kid extends JData {
String getName();
void setName(String name);
}
@Builder(toBuilder = true)
public record Kid(JObjectKey key, String name) implements JData {
}

View File

@@ -2,13 +2,8 @@ package com.usatiuk.dhfs.objects.data;
import com.usatiuk.objects.common.runtime.JData;
import com.usatiuk.objects.common.runtime.JObjectKey;
import lombok.Builder;
public interface Parent extends JData {
String getLastName();
void setLastName(String lastName);
JObjectKey getKidKey();
void setKidKey(JObjectKey kid);
}
@Builder(toBuilder = true)
public record Parent(JObjectKey key, String name) implements JData {
}

View File

@@ -17,7 +17,6 @@
<module>autoprotomap</module>
<module>objects</module>
<module>utils</module>
<module>objects-alloc</module>
<module>objects-common</module>
</modules>