diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index adb8dbe3..bebe5e79 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -44,6 +44,11 @@ jobs: with: java-version: ${{matrix.java}} distribution: 'temurin' + - name: Get binary for testing cross-compatibility with opencmw-cpp + env: + GH_TOKEN: ${{ github.token }} + continue-on-error: true + run: gh release download -R fair-acc/opencmw-cpp compatiblity-test-util -p CompatibilityTest -D . - name: Test with Maven run: mvn -Dgpg.skip=true --no-transfer-progress --batch-mode -Drevision=${REVISION} -Dsha1=${SHA1} -Dchangelist=${CHANGELIST} package env: diff --git a/client/src/test/java/io/opencmw/client/OpenCmwCppInteropTest.java b/client/src/test/java/io/opencmw/client/OpenCmwCppInteropTest.java new file mode 100644 index 00000000..186ffaf0 --- /dev/null +++ b/client/src/test/java/io/opencmw/client/OpenCmwCppInteropTest.java @@ -0,0 +1,293 @@ +package io.opencmw.client; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.time.Duration; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.LockSupport; + +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.zeromq.*; + +import io.opencmw.EventStore; +import io.opencmw.Filter; +import io.opencmw.MimeType; +import io.opencmw.client.DataSourcePublisher.NotificationListener; +import io.opencmw.filter.EvtTypeFilter; +import io.opencmw.serialiser.IoClassSerialiser; +import io.opencmw.serialiser.spi.BinarySerialiser; +import io.opencmw.serialiser.spi.FastByteBuffer; + +/** + * @author Alexander Krimm + */ +@Timeout(20) +class OpenCmwCppInteropTest { + private static final Logger LOGGER = LoggerFactory.getLogger(OpenCmwCppInteropTest.class); + private EventStore eventStore; + private DataSourcePublisher dataSourcePublisher; + private final URI brokerAddress = URI.create("mdp://localhost:12345"); + private final URI brokerPubAddress = URI.create("mds://localhost:12346"); + + @BeforeEach + void setupBrokerAndEventStore() throws IOException { + eventStore = EventStore.getFactory().setFilterConfig(EvtTypeFilter.class).build(); + dataSourcePublisher = new DataSourcePublisher(null, eventStore, null, null, "testOpenCmwPublisher"); + } + + @Test + void testSerialiserCompatibility() throws Exception { + Assumptions.assumeTrue(new File("../CompatibilityTest").exists()); // skip if the test binary is not available + int value = 42; + int arraySize = 3; + Process cppOpencmw = new ProcessBuilder("../CompatibilityTest", "serialise", "--value", value + "", "--array-size", arraySize + "").start(); + + final TestData testData = new TestData(); + testData.booleanValue = value % 2 == 0; + testData.int8Value = (byte) value; + testData.int16Value = (short) value; + testData.int32Value = value; + testData.int64Value = value; + testData.floatValue = value; + testData.doubleValue = value; + testData.charValue = (char) value; + testData.stringValue = "test 42"; + testData.boolArray = new boolean[] { true, false, true }; + testData.int8Array = new byte[] { 42, 43, 44 }; + testData.int16Array = new short[] { 42, 43, 44 }; + testData.int32Array = new int[] { 42, 43, 44 }; + testData.int64Array = new long[] { 42L, 43L, 44L }; + testData.floatArray = new float[] { 42.0f, 43.0f, 44.0f }; + testData.doubleArray = new double[] { 42.0, 43.0, 44.0 }; + testData.charArray = new char[] { (char) 42, (char) 43, (char) 44 }; + testData.stringArray = new String[] { "test42", "test43", "test44" }; + + final FastByteBuffer buffer = new FastByteBuffer(10000); + // buffer.setAutoResize(true); + IoClassSerialiser serialiser = new IoClassSerialiser(buffer, BinarySerialiser.class); + buffer.reset(); // '0' writing at start of buffer + serialiser.serialiseObject(testData); + Files.write(new File("java.hex").toPath(), Arrays.copyOfRange(buffer.elements(), 0, buffer.position())); + + cppOpencmw.waitFor(); + byte[] opencmwCppString = cppOpencmw.getInputStream().readAllBytes(); + Files.write(new File("cpp.hex").toPath(), opencmwCppString); + + // this assertion does not work, because the name of the Object is different between java and cpp, so all indices are shifted by the difference in object name length + // assertEquals(opencmwCppString, opencmwJavaString); + + final FastByteBuffer cppSerialisedBuffer = FastByteBuffer.wrap(opencmwCppString); + cppSerialisedBuffer.reset(); + IoClassSerialiser cppSerialisedDeserialiser = new IoClassSerialiser(cppSerialisedBuffer, BinarySerialiser.class); + TestData cppDeserialisedObject = cppSerialisedDeserialiser.deserialiseObject(TestData.class); + assertEquals(testData, cppDeserialisedObject); + } + + @Test + void testSubscriptionWithListener() throws Exception { + Assumptions.assumeTrue(new File("../CompatibilityTest").exists()); // skip if the test binary is not available + int port = brokerAddress.getPort(); + int portMds = brokerPubAddress.getPort(); + Process cppOpencmw = new ProcessBuilder("../CompatibilityTest", "serve", "--port", port + "", "--port-mds", portMds + "").inheritIO().start(); + assertNotNull(dataSourcePublisher.getRawDataEventStore()); + final Queue updates = new ArrayBlockingQueue<>(20); + eventStore.register((event, seq, last) -> { + if (!event.throwables.isEmpty()) { + System.err.println("errors"); + event.throwables.forEach((Throwable e) -> System.err.println(e.getMessage())); + } else { + if (event.payload.getType() == TestData.class) { + System.out.println("got update with payload: doubleValue = " + event.payload.get(TestData.class).doubleValue); + } else { + System.err.println("unexpected payload type" + event.payload.getType().getSimpleName()); + } + } + }); + + eventStore.start(); + dataSourcePublisher.start(); + LockSupport.parkNanos(Duration.ofMillis(200).toNanos()); + + try (final DataSourcePublisher.Client client = dataSourcePublisher.getClient()) { + final URI requestURI = URI.create(brokerPubAddress + "/testProperty"); + final TestContext requestContext = new TestContext(); + // requestContext.contextA = "P1"; + // requestContext.contextB = "T2"; + requestContext.contentType = MimeType.BINARY; + final NotificationListener listener = new NotificationListener<>() { + @Override + public void dataUpdate(final TestData updatedObject, final TestContext contextObject) { + // assertEquals(new TestContext(), contextObject); + updates.add(updatedObject); + System.err.println("update"); + } + + @Override + public void updateException(final Throwable exception) { + fail("Unexpected exception notification", exception); + } + }; + LOGGER.atInfo().addArgument(requestURI).log("subscribing to endpoint: {}"); + final String reqId = client.subscribe(requestURI, TestData.class, requestContext, TestContext.class, listener); + + LockSupport.parkNanos(Duration.ofMillis(10).toNanos()); + + // check if all notifications were received + LOGGER.atDebug().log("Waiting for subscription updates to be received"); + Awaitility.await().atMost(Duration.ofSeconds(15)).until(() -> updates.size() >= 4); + LOGGER.atDebug().log("Subscription updates complete"); + + LOGGER.atDebug().addArgument(reqId).log("Unsubscribing: {}"); + client.unsubscribe(reqId); + } + + // stop event store + eventStore.stop(); + dataSourcePublisher.stop(); + cppOpencmw.destroy(); + } + + @Test + void testGetRequest() throws InterruptedException, ExecutionException, TimeoutException, IOException { + Assumptions.assumeTrue(new File("../CompatibilityTest").exists()); // skip if the test binary is not available + int port = brokerAddress.getPort(); + int portMds = brokerPubAddress.getPort(); + Process cppOpencmw = new ProcessBuilder("../CompatibilityTest", "serve", "--port", port + "", "--port-mds", portMds + "").inheritIO().start(); + eventStore.start(); + dataSourcePublisher.start(); + LockSupport.parkNanos(Duration.ofMillis(200).toNanos()); + + // get request + final URI requestURI = URI.create(brokerAddress + "/testProperty?contentType=application/octet-stream&contextA=test&contextB=asdf"); + LOGGER.atInfo().addArgument(requestURI).log("requesting GET from endpoint: {}"); + final Future future; + try (final DataSourcePublisher.Client client = dataSourcePublisher.getClient()) { + future = client.get(requestURI, null, TestData.class); // uri_without_query oder serviceName + resolver, requestContext, type + } catch (Exception e) { + System.err.println(e.getMessage()); + throw e; + } + + // assert result + final TestData result = future.get(1000, TimeUnit.MILLISECONDS); + + System.out.println(result.doubleValue); + + eventStore.stop(); + dataSourcePublisher.stop(); + cppOpencmw.destroy(); + } + + @Test + void testGetRequestWithAnnotations() throws InterruptedException, ExecutionException, TimeoutException, IOException { + Assumptions.assumeTrue(new File("../CompatibilityTest").exists()); // skip if the test binary is not available + int port = brokerAddress.getPort(); + int portMds = brokerPubAddress.getPort(); + Process cppOpencmw = new ProcessBuilder("../CompatibilityTest", "serve", "--port", port + "", "--port-mds", portMds + "").inheritIO().start(); + eventStore.start(); + dataSourcePublisher.start(); + LockSupport.parkNanos(Duration.ofMillis(200).toNanos()); + + // get request + final URI requestURI = URI.create(brokerAddress + "/annotatedProperty?contentType=application/octet-stream&contextA=test&contextB=asdf"); + LOGGER.atInfo().addArgument(requestURI).log("requesting GET from endpoint: {}"); + final Future future; + try (final DataSourcePublisher.Client client = dataSourcePublisher.getClient()) { + future = client.get(requestURI, null, TestData.class); // uri_without_query oder serviceName + resolver, requestContext, type + } catch (Exception e) { + System.err.println(e.getMessage()); + throw e; + } + + // assert result + final TestData result = future.get(1000, TimeUnit.MILLISECONDS); + + System.out.println(result.doubleValue); + + eventStore.stop(); + dataSourcePublisher.stop(); + cppOpencmw.destroy(); + } + + public static class TestData { + boolean booleanValue; + byte int8Value; + short int16Value; + int int32Value; + long int64Value; + float floatValue; + double doubleValue; + char charValue; + String stringValue; + boolean[] boolArray; + byte[] int8Array; + short[] int16Array; + int[] int32Array; + long[] int64Array; + float[] floatArray; + double[] doubleArray; + char[] charArray; + String[] stringArray; + // TestData nestedData; + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TestData td2)) + return false; + return booleanValue == td2.booleanValue && int8Value == td2.int8Value && int16Value == td2.int16Value && int32Value == td2.int32Value && int64Value == td2.int64Value && floatValue == td2.floatValue && doubleValue == td2.doubleValue && charValue == td2.charValue && stringValue.equals(td2.stringValue) && Arrays.equals(boolArray, td2.boolArray) && Arrays.equals(int8Array, td2.int8Array) && Arrays.equals(int16Array, td2.int16Array) && Arrays.equals(int32Array, td2.int32Array) && Arrays.equals(int64Array, td2.int64Array) && Arrays.equals(floatArray, td2.floatArray) && Arrays.equals(doubleArray, td2.doubleArray) && Arrays.equals(charArray, td2.charArray) && Arrays.equals(stringArray, td2.stringArray); + } + + @Override + public int hashCode() { + return Objects.hash(booleanValue, int8Value, int16Value, int32Value, int64Value, floatValue, doubleValue, charValue, stringValue, Arrays.hashCode(boolArray), Arrays.hashCode(int8Array), Arrays.hashCode(int16Array), Arrays.hashCode(int32Array), Arrays.hashCode(int64Array), Arrays.hashCode(floatArray), Arrays.hashCode(doubleArray), Arrays.hashCode(charArray), Arrays.hashCode(stringArray)); + } + } + + public static class TestContext implements Filter { + public MimeType contentType = MimeType.BINARY; + + @Override + public void clear() { + contentType = MimeType.BINARY; + } + + @Override + public void copyTo(Filter other) { + if (!(other instanceof TestContext otherContext)) { + throw new RuntimeException("Trying to copy TestContext into a different filter type"); + } + otherContext.contentType = contentType; + } + + @Override + public String getKey() { + return ""; + } + + @Override + public String getValue() { + return ""; + } + + @Override + public Filter get(String ctxString) { + return null; + } + + @Override + public boolean matches(Filter other) { + return false; + } + } +} \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index 9ab058e3..ab6eeed4 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -50,6 +50,11 @@ docopt ${version.docopt} + + com.jsoniter + jsoniter + ${version.jsoniter} + diff --git a/serialiser/src/main/java/io/opencmw/serialiser/DataType.java b/serialiser/src/main/java/io/opencmw/serialiser/DataType.java index 7ff0705a..dd79f865 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/DataType.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/DataType.java @@ -21,12 +21,12 @@ import de.gsi.dataset.spi.utils.MultiArrayShort; /** - * Enum definition for data primitives in the context of serialisation and includes definitions for: + * Enum definition for data primitives in the context of serialisation and includes definitions for: *
    - *
  • primitives (byte, short, ..., float, double, and String), and - *
  • arrays thereof (ie. byte[], short[], ..., float[], double[], and String[]), as well as + *
  • primitives (byte, short, ..., float, double, and String), and + *
  • arrays thereof (ie. byte[], short[], ..., float[], double[], and String[]), as well as *
  • complex objects implementing Collections (ie. Set, List, Queues), Enums or Maps. - *
+ * * Any other complex data objects can be stored/extended using the {@link DataType#OTHER OTHER} sub-type. * *

@@ -50,7 +50,7 @@ public enum DataType { LONG(5, "long", "long", 8, Cat.SINGLE_VALUE, long.class, Long.class), FLOAT(6, "float", "float", 4, Cat.SINGLE_VALUE, float.class, Float.class), DOUBLE(7, "double", "double", 8, Cat.SINGLE_VALUE, double.class, Double.class), - CHAR(8, "char", "char", 2, Cat.SINGLE_VALUE, char.class, Character.class), + CHAR(8, "char", "char", 1, Cat.SINGLE_VALUE, char.class, Character.class), STRING(9, "string", "java.lang.String", 1, Cat.ARRAY, String.class, URI.class), // array of primitive types @@ -61,7 +61,7 @@ public enum DataType { LONG_ARRAY(105, "long_array", "[J", 8, Cat.ARRAY, long[].class, Long[].class, MultiArrayLong.class), FLOAT_ARRAY(106, "float_array", "[F", 4, Cat.ARRAY, float[].class, Float[].class, MultiArrayFloat.class), DOUBLE_ARRAY(107, "double_array", "[D", 8, Cat.ARRAY, double[].class, Double[].class, MultiArrayDouble.class), - CHAR_ARRAY(108, "char_array", "[C", 2, Cat.ARRAY, char[].class, Character[].class, MultiArrayChar.class), + CHAR_ARRAY(108, "char_array", "[C", 1, Cat.ARRAY, char[].class, Character[].class, MultiArrayChar.class), STRING_ARRAY(109, "string_array", "[java.lang.String", 1, Cat.ARRAY, String[].class, String[].class, MultiArrayObject.class), // complex objects diff --git a/serialiser/src/main/java/io/opencmw/serialiser/FieldDescription.java b/serialiser/src/main/java/io/opencmw/serialiser/FieldDescription.java index 58d155c6..a2a15e05 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/FieldDescription.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/FieldDescription.java @@ -38,20 +38,7 @@ public interface FieldDescription { */ String getFieldDescription(); - /** - * Return optional meta data tag describing the 'direction' of this data field. - * The information encodes the source servicedevelopers intend to the receiving user whether the field can be, for example, - * modified (get/set), set-only, or read-only, or attach any other similar information. Encoding/interpretation is - * left ad-lib to the source service developer. - * - * @return optional meta data (N.B. can be empty String). - */ - String getFieldDirection(); - - /** - * @return optional meta data describing the group/set this data field belongs to (N.B. empty String corresponds to 'all') - */ - List getFieldGroups(); + byte getFieldModifier(); /** * @return the data field's name @@ -69,6 +56,11 @@ public interface FieldDescription { */ String getFieldUnit(); + /** + * @return optional meta data tag describing the field's quantity or similar (N.B. can be empty String) + */ + String getFieldQuantity(); + /** * @return for a hierarchical/nested data structure refers to the parent this field belongs to (N.B. can be null if there isn't a parent, e.g. for a root element) */ diff --git a/serialiser/src/main/java/io/opencmw/serialiser/annotations/MetaInfo.java b/serialiser/src/main/java/io/opencmw/serialiser/annotations/MetaInfo.java index 8659a6f0..8f80f7ef 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/annotations/MetaInfo.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/annotations/MetaInfo.java @@ -9,7 +9,7 @@ @Target({ ElementType.FIELD, ElementType.TYPE }) public @interface MetaInfo { String unit() default ""; + String quantity() default ""; String description() default ""; - String direction() default ""; - String[] groups() default ""; + byte modifier() default 0; } \ No newline at end of file diff --git a/serialiser/src/main/java/io/opencmw/serialiser/annotations/Groups.java b/serialiser/src/main/java/io/opencmw/serialiser/annotations/Modifier.java similarity index 83% rename from serialiser/src/main/java/io/opencmw/serialiser/annotations/Groups.java rename to serialiser/src/main/java/io/opencmw/serialiser/annotations/Modifier.java index 585c4e2c..7f474813 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/annotations/Groups.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/annotations/Modifier.java @@ -7,6 +7,6 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.TYPE }) -public @interface Groups { - String[] value() default ""; +public @interface Modifier { + byte value() default 0; } diff --git a/serialiser/src/main/java/io/opencmw/serialiser/annotations/Direction.java b/serialiser/src/main/java/io/opencmw/serialiser/annotations/Quantity.java similarity index 91% rename from serialiser/src/main/java/io/opencmw/serialiser/annotations/Direction.java rename to serialiser/src/main/java/io/opencmw/serialiser/annotations/Quantity.java index 815da5d7..7126ae25 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/annotations/Direction.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/annotations/Quantity.java @@ -7,6 +7,6 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.TYPE }) -public @interface Direction { +public @interface Quantity { String value() default ""; } diff --git a/serialiser/src/main/java/io/opencmw/serialiser/spi/BinarySerialiser.java b/serialiser/src/main/java/io/opencmw/serialiser/spi/BinarySerialiser.java index 6f813dd3..5e73afd4 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/spi/BinarySerialiser.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/spi/BinarySerialiser.java @@ -423,14 +423,13 @@ public WireDataFieldDescription getFieldHeader() { lastFieldHeader.setFieldUnit(buffer.getString()); } if (buffer.position() < dataStartPosition) { - lastFieldHeader.setFieldDescription(buffer.getString()); + lastFieldHeader.setFieldQuantity(buffer.getString()); } if (buffer.position() < dataStartPosition) { - lastFieldHeader.setFieldDirection(buffer.getString()); + lastFieldHeader.setFieldDescription(buffer.getString()); } if (buffer.position() < dataStartPosition) { - final String[] fieldGroups = buffer.getStringArray(); - lastFieldHeader.setFieldGroups(fieldGroups == null ? Collections.emptyList() : Arrays.asList(fieldGroups)); + lastFieldHeader.setFieldModifier(buffer.getByte()); } } else { buffer.position(dataStartPosition); @@ -1460,10 +1459,9 @@ public WireDataFieldDescription putFieldHeader(final FieldDescription fieldDescr if (isPutFieldMetaData() && fieldDescription.isAnnotationPresent() && dataType != DataType.END_MARKER) { buffer.putString(fieldDescription.getFieldUnit()); + buffer.putString(fieldDescription.getFieldQuantity()); buffer.putString(fieldDescription.getFieldDescription()); - buffer.putString(fieldDescription.getFieldDirection()); - final String[] groups = fieldDescription.getFieldGroups().toArray(new String[0]); - buffer.putStringArray(groups, groups.length); + buffer.putByte(fieldDescription.getFieldModifier()); } // -- offset dataStart calculations @@ -1476,9 +1474,9 @@ public WireDataFieldDescription putFieldHeader(final FieldDescription fieldDescr lastFieldHeader = new WireDataFieldDescription(this, parent, fieldDescription.getFieldName(), dataType, headerStart, dataStartOffset, dataSize); if (isPutFieldMetaData() && fieldDescription.isAnnotationPresent()) { lastFieldHeader.setFieldUnit(fieldDescription.getFieldUnit()); + lastFieldHeader.setFieldQuantity(fieldDescription.getFieldQuantity()); lastFieldHeader.setFieldDescription(fieldDescription.getFieldDescription()); - lastFieldHeader.setFieldDirection(fieldDescription.getFieldDirection()); - lastFieldHeader.setFieldGroups(fieldDescription.getFieldGroups()); + lastFieldHeader.setFieldModifier(fieldDescription.getFieldModifier()); } return lastFieldHeader; } diff --git a/serialiser/src/main/java/io/opencmw/serialiser/spi/ByteBuffer.java b/serialiser/src/main/java/io/opencmw/serialiser/spi/ByteBuffer.java index a9079be4..ee9db028 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/spi/ByteBuffer.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/spi/ByteBuffer.java @@ -14,7 +14,7 @@ public class ByteBuffer implements IoBuffer { public static final int SIZE_OF_BOOLEAN = 1; public static final int SIZE_OF_BYTE = 1; public static final int SIZE_OF_SHORT = 2; - public static final int SIZE_OF_CHAR = 2; + public static final int SIZE_OF_CHAR = 1; public static final int SIZE_OF_INT = 4; public static final int SIZE_OF_LONG = 8; public static final int SIZE_OF_FLOAT = 4; @@ -119,12 +119,12 @@ public byte[] getByteArray(final byte[] dst, final int length) { @Override public char getChar() { - return nioByteBuffer.getChar(); + return (char) (nioByteBuffer.get() & 0xFF); } @Override public char getChar(final int position) { - return nioByteBuffer.getChar(position); + return (char) (nioByteBuffer.get(position) & 0xFF); } @Override @@ -374,12 +374,12 @@ public void putByteArray(final byte[] src, final int n) { @Override public void putChar(final char value) { - nioByteBuffer.putChar(value); + nioByteBuffer.put((byte) value); } @Override public void putChar(final int position, final char value) { - nioByteBuffer.putChar(position, value); + nioByteBuffer.put(position, (byte) value); } @Override @@ -392,7 +392,7 @@ public void putCharArray(final char[] src, final int n) { return; } for (int i = 0; i < nElements; i++) { - nioByteBuffer.putChar(src[i]); + nioByteBuffer.put((byte) (src[i] & 0xFF)); } } diff --git a/serialiser/src/main/java/io/opencmw/serialiser/spi/ClassFieldDescription.java b/serialiser/src/main/java/io/opencmw/serialiser/spi/ClassFieldDescription.java index d0437853..a79dda68 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/spi/ClassFieldDescription.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/spi/ClassFieldDescription.java @@ -16,11 +16,8 @@ import io.opencmw.serialiser.DataType; import io.opencmw.serialiser.FieldDescription; import io.opencmw.serialiser.FieldSerialiser; -import io.opencmw.serialiser.annotations.Description; -import io.opencmw.serialiser.annotations.Direction; -import io.opencmw.serialiser.annotations.Groups; -import io.opencmw.serialiser.annotations.MetaInfo; -import io.opencmw.serialiser.annotations.Unit; +import io.opencmw.serialiser.annotations.*; +import io.opencmw.serialiser.annotations.Modifier; import io.opencmw.serialiser.utils.ClassUtils; /** @@ -34,9 +31,9 @@ public class ClassFieldDescription implements FieldDescription { private final String fieldName; private final String fieldNameRelative; private final String fieldUnit; + private final String fieldQuantity; private final String fieldDescription; - private final String fieldDirection; - private final List fieldGroups; + private final byte fieldModifier; private final boolean annotationPresent; private final ClassFieldDescription parent; private final List children = new ArrayList<>(); @@ -119,28 +116,28 @@ protected ClassFieldDescription(final Class referenceClass, final Field field // read annotation values AnnotatedElement annotatedElement = field == null ? referenceClass : field; fieldUnit = getFieldUnit(annotatedElement); + fieldQuantity = getFieldQuantity(annotatedElement); fieldDescription = getFieldDescription(annotatedElement); - fieldDirection = getFieldDirection(annotatedElement); - fieldGroups = getFieldGroups(annotatedElement); + fieldModifier = getFieldModifier(annotatedElement); - annotationPresent = fieldUnit != null || fieldDescription != null || fieldDirection != null || !fieldGroups.isEmpty(); + annotationPresent = fieldUnit != null || fieldQuantity != null || fieldDescription != null; typeName = ClassUtils.translateClassName(classType.getTypeName()).intern(); final int lastDot = typeName.lastIndexOf('.'); typeNameSimple = typeName.substring(lastDot < 0 ? 0 : lastDot + 1); - modPublic = Modifier.isPublic(modifierID); - modProtected = Modifier.isProtected(modifierID); - modPrivate = Modifier.isPrivate(modifierID); - - modAbstract = Modifier.isAbstract(modifierID); - modStatic = Modifier.isStatic(modifierID); - modFinal = Modifier.isFinal(modifierID); - modTransient = Modifier.isTransient(modifierID); - modVolatile = Modifier.isVolatile(modifierID); - modSynchronized = Modifier.isSynchronized(modifierID); - modNative = Modifier.isNative(modifierID); - modStrict = Modifier.isStrict(modifierID); + modPublic = java.lang.reflect.Modifier.isPublic(modifierID); + modProtected = java.lang.reflect.Modifier.isProtected(modifierID); + modPrivate = java.lang.reflect.Modifier.isPrivate(modifierID); + + modAbstract = java.lang.reflect.Modifier.isAbstract(modifierID); + modStatic = java.lang.reflect.Modifier.isStatic(modifierID); + modFinal = java.lang.reflect.Modifier.isFinal(modifierID); + modTransient = java.lang.reflect.Modifier.isTransient(modifierID); + modVolatile = java.lang.reflect.Modifier.isVolatile(modifierID); + modSynchronized = java.lang.reflect.Modifier.isSynchronized(modifierID); + modNative = java.lang.reflect.Modifier.isNative(modifierID); + modStrict = java.lang.reflect.Modifier.isStrict(modifierID); modInterface = classType.isInterface(); // additional fields @@ -177,7 +174,7 @@ public Object allocateMemberClassField(final Object fieldParent) { try { // need to allocate new class object final Object newFieldObj; - if (this.classType.getDeclaringClass() == null || Modifier.isStatic(this.classType.getModifiers())) { + if (this.classType.getDeclaringClass() == null || java.lang.reflect.Modifier.isStatic(this.classType.getModifiers())) { final Constructor constr = this.classType.getDeclaredConstructor(); newFieldObj = constr.newInstance(); } else { @@ -297,13 +294,8 @@ public String getFieldDescription() { } @Override - public String getFieldDirection() { - return fieldDirection; - } - - @Override - public List getFieldGroups() { - return fieldGroups; + public byte getFieldModifier() { + return fieldModifier; } /** @@ -335,6 +327,11 @@ public String getFieldUnit() { return fieldUnit; } + @Override + public String getFieldQuantity() { + return fieldQuantity; + } + /** * @return field type strings (e.g. for the class Map<Integer,String> this returns * '<java.lang.Integer,java.lang.String>' @@ -401,7 +398,7 @@ public String getModifierString() { if (modifierStr == null) { // initialise only on a need to basis // for performance reasons - modifierStr = Modifier.toString(modifierID).intern(); + modifierStr = java.lang.reflect.Modifier.toString(modifierID).intern(); } return modifierStr; } @@ -650,10 +647,10 @@ protected static void printClassStructure(final ClassFieldDescription field, fin if (field.isAnnotationPresent()) { LOGGER.atInfo().addArgument(mspace).addArgument(isSerialisable ? " " : "//") // .addArgument(field.getFieldUnit()) + .addArgument(field.getFieldQuantity()) .addArgument(field.getFieldDescription()) - .addArgument(field.getFieldDirection()) - .addArgument(field.getFieldGroups()) - .log("{} {} "); + .addArgument(field.getFieldModifier()) + .log("{} {} "); } field.getChildren().forEach(f -> printClassStructure((ClassFieldDescription) f, fullView, recursionLevel + 1)); @@ -662,52 +659,48 @@ protected static void printClassStructure(final ClassFieldDescription field, fin private static String getFieldDescription(final AnnotatedElement annotatedElement) { final MetaInfo[] annotationMeta = annotatedElement.getAnnotationsByType(MetaInfo.class); - if (annotationMeta != null && annotationMeta.length > 0) { + if (annotationMeta.length > 0) { return annotationMeta[0].description().intern(); } final Description[] annotationDescription = annotatedElement.getAnnotationsByType(Description.class); - if (annotationDescription != null && annotationDescription.length > 0) { + if (annotationDescription.length > 0) { return annotationDescription[0].value().intern(); } return null; } - private static String getFieldDirection(final AnnotatedElement annotatedElement) { + private static byte getFieldModifier(final AnnotatedElement annotatedElement) { final MetaInfo[] annotationMeta = annotatedElement.getAnnotationsByType(MetaInfo.class); - if (annotationMeta != null && annotationMeta.length > 0) { - return annotationMeta[0].direction().intern(); + if (annotationMeta.length > 0) { + return annotationMeta[0].modifier(); } - final Direction[] annotationDirection = annotatedElement.getAnnotationsByType(Direction.class); - if (annotationDirection != null && annotationDirection.length > 0) { - return annotationDirection[0].value().intern(); + final Modifier[] annotationModifier = annotatedElement.getAnnotationsByType(Modifier.class); + if (annotationModifier.length > 0) { + return annotationModifier[0].value(); } - return null; + return 0; } - private static List getFieldGroups(final AnnotatedElement annotatedElement) { + private static String getFieldUnit(final AnnotatedElement annotatedElement) { final MetaInfo[] annotationMeta = annotatedElement.getAnnotationsByType(MetaInfo.class); - if (annotationMeta != null && annotationMeta.length > 0) { - return Arrays.asList(annotationMeta[0].groups()); + if (annotationMeta.length > 0) { + return annotationMeta[0].unit().intern(); } - final Groups[] annotationGroups = annotatedElement.getAnnotationsByType(Groups.class); - if (annotationGroups != null && annotationGroups.length > 0) { - final List ret = new ArrayList<>(annotationGroups[0].value().length); - for (int i = 0; i < annotationGroups[0].value().length; i++) { - ret.add(annotationGroups[0].value()[i].intern()); - } - return ret; + final Unit[] annotationUnit = annotatedElement.getAnnotationsByType(Unit.class); + if (annotationUnit.length > 0) { + return annotationUnit[0].value().intern(); } - return Collections.emptyList(); + return null; } - private static String getFieldUnit(final AnnotatedElement annotatedElement) { + private static String getFieldQuantity(final AnnotatedElement annotatedElement) { final MetaInfo[] annotationMeta = annotatedElement.getAnnotationsByType(MetaInfo.class); - if (annotationMeta != null && annotationMeta.length > 0) { - return annotationMeta[0].unit().intern(); + if (annotationMeta.length > 0) { + return annotationMeta[0].quantity().intern(); } - final Unit[] annotationUnit = annotatedElement.getAnnotationsByType(Unit.class); - if (annotationUnit != null && annotationUnit.length > 0) { - return annotationUnit[0].value().intern(); + final Quantity[] annotationQuantity = annotatedElement.getAnnotationsByType(Quantity.class); + if (annotationQuantity.length > 0) { + return annotationQuantity[0].value().intern(); } return null; } diff --git a/serialiser/src/main/java/io/opencmw/serialiser/spi/FastByteBuffer.java b/serialiser/src/main/java/io/opencmw/serialiser/spi/FastByteBuffer.java index fec64c78..da62bf21 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/spi/FastByteBuffer.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/spi/FastByteBuffer.java @@ -3,6 +3,7 @@ import static sun.misc.Unsafe.*; // NOSONAR NOPMD not an issue: contained and performance-related use import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -40,7 +41,7 @@ public class FastByteBuffer implements IoBuffer { public static final int SIZE_OF_BOOLEAN = 1; public static final int SIZE_OF_BYTE = 1; public static final int SIZE_OF_SHORT = 2; - public static final int SIZE_OF_CHAR = 2; + public static final int SIZE_OF_CHAR = 1; public static final int SIZE_OF_INT = 4; public static final int SIZE_OF_LONG = 8; public static final int SIZE_OF_FLOAT = 4; @@ -276,7 +277,7 @@ public ByteArrayCache getByteArrayCache() { @Override public char getChar() { checkAvailable(SIZE_OF_CHAR); - final char value = unsafe.getChar(buffer, (long) ARRAY_CHAR_BASE_OFFSET + intPos); + final char value = (char) (unsafe.getByte(buffer, (long) ARRAY_CHAR_BASE_OFFSET + intPos) & 0xFF); intPos += SIZE_OF_CHAR; return value; @@ -285,7 +286,7 @@ public char getChar() { @Override public char getChar(final int position) { checkAvailableAbsolute(position + SIZE_OF_CHAR); - return unsafe.getChar(buffer, (long) ARRAY_BYTE_BASE_OFFSET + position); + return (char) (unsafe.getByte(buffer, (long) ARRAY_BYTE_BASE_OFFSET + position) & 0xFF); } @Override @@ -296,8 +297,9 @@ public char[] getCharArray(final char[] dst, final int length) { final int bytesToCopy = arraySize * SIZE_OF_CHAR; checkAvailable(bytesToCopy); - copyMemory(buffer, ARRAY_BYTE_BASE_OFFSET + intPos, values, ARRAY_SHORT_BASE_OFFSET, bytesToCopy); - intPos += bytesToCopy; + for (int i = 0; i < bytesToCopy; ++i) { + values[i] = getChar(); + } return values; } @@ -600,14 +602,19 @@ public void putByteArray(final byte[] values, final int n) { @Override public void putChar(final char value) { ensureAdditionalCapacity(SIZE_OF_CHAR); - unsafe.putChar(buffer, (long) ARRAY_BYTE_BASE_OFFSET + intPos, value); + unsafe.putByte(buffer, (long) ARRAY_BYTE_BASE_OFFSET + intPos, (byte) value); intPos += SIZE_OF_CHAR; } @Override public void putChar(final int position, final char value) { ensureCapacity(position + SIZE_OF_CHAR); - unsafe.putChar(buffer, (long) ARRAY_BYTE_BASE_OFFSET + position, value); + byte[] bytes = String.valueOf(value).getBytes(StandardCharsets.UTF_8); + if (bytes.length != 1) { + throw new IllegalArgumentException("encountered non-ascii character, which would have to be represented by multiple bytes"); + } + unsafe.putByte(buffer, (long) ARRAY_BYTE_BASE_OFFSET + position, bytes[0]); + intPos += SIZE_OF_CHAR; } @Override @@ -618,8 +625,9 @@ public void putCharArray(final char[] values, final int n) { final int bytesToCopy = nElements * SIZE_OF_CHAR; ensureAdditionalCapacity(bytesToCopy + SIZE_OF_INT); putInt(nElements); // strided-array size - copyMemory(values, arrayOffset, buffer, arrayOffset + intPos, bytesToCopy); - intPos += bytesToCopy; + for (int i = 0; i < nElements; i++) { // individually copy chars as java uses 2 bytes per char and c++ just a single char + putChar(values[i]); + } } @Override diff --git a/serialiser/src/main/java/io/opencmw/serialiser/spi/WireDataFieldDescription.java b/serialiser/src/main/java/io/opencmw/serialiser/spi/WireDataFieldDescription.java index db225f38..a6a52337 100644 --- a/serialiser/src/main/java/io/opencmw/serialiser/spi/WireDataFieldDescription.java +++ b/serialiser/src/main/java/io/opencmw/serialiser/spi/WireDataFieldDescription.java @@ -29,9 +29,9 @@ public class WireDataFieldDescription implements FieldDescription { // local references to source buffer needed for parsing private final IoSerialiser ioSerialiser; private String fieldUnit; + private String fieldQuantity; private String fieldDescription; - private String fieldDirection; - private List fieldGroups; + private byte fieldModifier; private int dataSize; /** @@ -125,22 +125,13 @@ public void setFieldDescription(final String fieldDescription) { this.fieldDescription = fieldDescription; } - @Override - public String getFieldDirection() { - return fieldDirection; - } - - public void setFieldDirection(final String fieldDirection) { - this.fieldDirection = fieldDirection; + public void setFieldModifier(byte modifier) { + this.fieldModifier = modifier; } @Override - public List getFieldGroups() { - return fieldGroups; - } - - public void setFieldGroups(final List fieldGroups) { - this.fieldGroups = fieldGroups; + public byte getFieldModifier() { + return this.fieldModifier; } @Override @@ -162,6 +153,15 @@ public void setFieldUnit(final String fieldUnit) { this.fieldUnit = fieldUnit; } + @Override + public String getFieldQuantity() { + return fieldQuantity; + } + + public void setFieldQuantity(final String fieldQuantity) { + this.fieldQuantity = fieldQuantity; + } + /** * @return raw ioSerialiser reference this field was retrieved with the position in the underlying IoBuffer at the to be read field * N.B. this is a safe convenience method and not performance optimised @@ -219,7 +219,7 @@ public Class getType() { @Override public boolean isAnnotationPresent() { - return fieldUnit != null || fieldDescription != null || fieldDirection != null || (fieldGroups != null && !fieldGroups.isEmpty()); + return fieldUnit != null || fieldQuantity != null || fieldDescription != null; } @Override @@ -244,10 +244,10 @@ protected static void printFieldStructure(final FieldDescription field, final in if (field.isAnnotationPresent()) { LOGGER.atInfo().addArgument(mspace) // .addArgument(field.getFieldUnit()) + .addArgument(field.getFieldQuantity()) .addArgument(field.getFieldDescription()) - .addArgument(field.getFieldDirection()) - .addArgument(field.getFieldGroups()) - .log("{} "); + .addArgument(field.getFieldModifier()) + .log("{} "); } field.getChildren().forEach(f -> printFieldStructure(f, recursionLevel + 1)); } diff --git a/serialiser/src/test/java/io/opencmw/serialiser/annotations/SerialiserAnnotationTests.java b/serialiser/src/test/java/io/opencmw/serialiser/annotations/SerialiserAnnotationTests.java index 13e1d64c..f8d8b9fb 100644 --- a/serialiser/src/test/java/io/opencmw/serialiser/annotations/SerialiserAnnotationTests.java +++ b/serialiser/src/test/java/io/opencmw/serialiser/annotations/SerialiserAnnotationTests.java @@ -32,20 +32,16 @@ void testAnnotationGeneration() { final FieldDescription energyField = classFieldDescription.findChildField("energy"); assertNotNull(energyField); assertEquals("GeV/u", energyField.getFieldUnit()); + assertEquals("energy", energyField.getFieldQuantity()); assertEquals("energy description", energyField.getFieldDescription()); - assertEquals("OUT", energyField.getFieldDirection()); - assertFalse(energyField.getFieldGroups().isEmpty()); - assertEquals("A", energyField.getFieldGroups().get(0)); + assertEquals(0, energyField.getFieldModifier()); final FieldDescription temperatureField = classFieldDescription.findChildField("temperature"); assertNotNull(temperatureField); assertEquals("°C", temperatureField.getFieldUnit()); + assertEquals("temperature", temperatureField.getFieldQuantity()); assertEquals("important temperature reading", temperatureField.getFieldDescription()); - assertEquals("OUT", temperatureField.getFieldDirection()); - assertFalse(temperatureField.getFieldGroups().isEmpty()); - assertEquals(2, temperatureField.getFieldGroups().size()); - assertEquals("A", temperatureField.getFieldGroups().get(0)); - assertEquals("B", temperatureField.getFieldGroups().get(1)); + assertEquals(0, temperatureField.getFieldModifier()); } @DisplayName("basic custom serialisation/deserialisation identity") @@ -70,36 +66,33 @@ void testCustomSerialiserIdentity(final Class bufferClass) t final FieldDescription energyField = serialiserFieldDescriptions.findChildField("energy"); assertNotNull(energyField); assertEquals("GeV/u", energyField.getFieldUnit()); + assertEquals("energy", energyField.getFieldQuantity()); assertEquals("energy description", energyField.getFieldDescription()); - assertEquals("OUT", energyField.getFieldDirection()); - assertFalse(energyField.getFieldGroups().isEmpty()); - assertEquals("A", energyField.getFieldGroups().get(0)); + assertEquals(0, energyField.getFieldModifier()); final FieldDescription temperatureField = serialiserFieldDescriptions.findChildField("temperature"); assertNotNull(temperatureField); assertEquals("°C", temperatureField.getFieldUnit()); + assertEquals("temperature", temperatureField.getFieldQuantity()); assertEquals("important temperature reading", temperatureField.getFieldDescription()); - assertEquals("OUT", temperatureField.getFieldDirection()); - assertFalse(temperatureField.getFieldGroups().isEmpty()); - assertEquals(2, temperatureField.getFieldGroups().size()); - assertEquals("A", temperatureField.getFieldGroups().get(0)); - assertEquals("B", temperatureField.getFieldGroups().get(1)); + assertEquals(0, temperatureField.getFieldModifier()); } @Description("this class is used to test field annotation") public static class AnnotatedDataClass { - @MetaInfo(unit = "GeV/u", description = "energy description", direction = "OUT", groups = "A") + @MetaInfo(unit = "GeV/u", quantity = "energy", description = "energy description", modifier = 0) public double energy; @Unit("°C") + @Quantity("temperature") @Description("important temperature reading") - @Direction("OUT") - @Groups({ "A", "B" }) + @Modifier(0) public double temperature; @Unit("V") + @Quantity("voltage") @Description("control variable") - @Direction("IN/OUT") + @Modifier(1) public double controlVariable; } } diff --git a/serialiser/src/test/java/io/opencmw/serialiser/spi/IoBufferTests.java b/serialiser/src/test/java/io/opencmw/serialiser/spi/IoBufferTests.java index cd9f71a0..06688982 100644 --- a/serialiser/src/test/java/io/opencmw/serialiser/spi/IoBufferTests.java +++ b/serialiser/src/test/java/io/opencmw/serialiser/spi/IoBufferTests.java @@ -212,7 +212,7 @@ void primitivesMixed(final Class bufferClass) throws NoSuchM buffer.putFloat(1.3e10f); buffer.putDouble(1.3e10f); buffer.putChar('@'); - buffer.putChar((char) 513); + // buffer.putChar((char) 513); buffer.putStringISO8859("Hello World!"); buffer.putString("Γειά σου Κόσμε!"); final long position = buffer.position(); @@ -228,7 +228,7 @@ void primitivesMixed(final Class bufferClass) throws NoSuchM assertEquals(1.3e10f, buffer.getFloat()); assertEquals(1.3e10f, buffer.getDouble()); assertEquals('@', buffer.getChar()); - assertEquals((char) 513, buffer.getChar()); + // assertEquals((char) 513, buffer.getChar()); assertEquals("Hello World!", buffer.getStringISO8859()); assertEquals("Γειά σου Κόσμε!", buffer.getString()); assertEquals(position, buffer.position()); @@ -286,10 +286,11 @@ void primitivesSimple(final Class bufferClass) throws NoSuch buffer.flip(); assertEquals('@', buffer.getChar()); - buffer.reset(); - buffer.putChar((char) 513); - buffer.flip(); - assertEquals((char) 513, buffer.getChar()); + // codepoints which require more than a single utf-8 byte are disabled + // buffer.reset(); + // buffer.putChar((char) 513); + // buffer.flip(); + // assertEquals((char) 513, buffer.getChar()); buffer.reset(); buffer.putString("Hello World!"); @@ -334,8 +335,9 @@ void primitivesSimpleInPlace(final Class bufferClass) throws buffer.putChar(7, '@'); assertEquals('@', buffer.getChar(7)); - buffer.putChar(7, (char) 513); - assertEquals((char) 513, buffer.getChar(7)); + // codepoints out of single utf-8 byte are disabled + // buffer.putChar(7, (char) 513); + // assertEquals((char) 513, buffer.getChar(7)); buffer.putString(8, "Hello World!"); assertEquals("Hello World!", buffer.getString(8)); diff --git a/server-rest/src/test/java/io/opencmw/server/rest/helper/ReplyDataType.java b/server-rest/src/test/java/io/opencmw/server/rest/helper/ReplyDataType.java index efc0cc0e..93d68bf1 100644 --- a/server-rest/src/test/java/io/opencmw/server/rest/helper/ReplyDataType.java +++ b/server-rest/src/test/java/io/opencmw/server/rest/helper/ReplyDataType.java @@ -5,23 +5,23 @@ import de.gsi.dataset.spi.utils.MultiArray; -@MetaInfo(description = "reply type class description", direction = "OUT") +@MetaInfo(description = "reply type class description") public class ReplyDataType { @MetaInfo(description = "ReplyDataType name to show up in the OpenAPI docs") public String name; public boolean booleanReturnType; public byte byteReturnType; public short shortReturnType; - @MetaInfo(description = "a return value", unit = "A", direction = "OUT", groups = { "A", "B" }) + @MetaInfo(description = "a return value", unit = "A") public int intReturnValue; public long longReturnValue; public byte[] byteArray; public MultiArray multiArray = MultiArray.wrap(new double[] { 1.0, 2.0, 3.0 }, 0, new int[] { 1 }); - @MetaInfo(description = "WR timing context", direction = "OUT", groups = { "A", "B" }) + @MetaInfo(description = "WR timing context") public TimingCtx timingCtx; - @MetaInfo(description = "LSA timing context", direction = "OUT", groups = { "A", "B" }) + @MetaInfo(description = "LSA timing context") public String lsaContext = ""; - @MetaInfo(description = "custom enum reply option", direction = "OUT", groups = { "A", "B" }) + @MetaInfo(description = "custom enum reply option") public ReplyOption replyOption = ReplyOption.REPLY_OPTION2; public ReplyDataType() { diff --git a/server-rest/src/test/java/io/opencmw/server/rest/helper/RequestDataType.java b/server-rest/src/test/java/io/opencmw/server/rest/helper/RequestDataType.java index c6a9a37c..b4a8921c 100644 --- a/server-rest/src/test/java/io/opencmw/server/rest/helper/RequestDataType.java +++ b/server-rest/src/test/java/io/opencmw/server/rest/helper/RequestDataType.java @@ -4,7 +4,7 @@ import io.opencmw.filter.TimingCtx; import io.opencmw.serialiser.annotations.MetaInfo; -@MetaInfo(description = "request type class description", direction = "IN") +@MetaInfo(description = "request type class description") public class RequestDataType { @MetaInfo(description = " RequestDataType name to show up in the OpenAPI docs") public String name = ""; diff --git a/server-rest/src/test/java/io/opencmw/server/rest/samples/BasicSample.java b/server-rest/src/test/java/io/opencmw/server/rest/samples/BasicSample.java index b1395b18..2fb6819f 100644 --- a/server-rest/src/test/java/io/opencmw/server/rest/samples/BasicSample.java +++ b/server-rest/src/test/java/io/opencmw/server/rest/samples/BasicSample.java @@ -69,13 +69,13 @@ public void run() { } } - @MetaInfo(description = "arbitrary request domain context object", direction = "IN") + @MetaInfo(description = "arbitrary request domain context object") public static class BasicRequestCtx { @MetaInfo(description = " optional 'name' OpenAPI documentation") public String name; } - @MetaInfo(description = "arbitrary reply domain object", direction = "OUT") + @MetaInfo(description = "arbitrary reply domain object") public static class ReplyData { @MetaInfo(description = " optional 'returnValue' OpenAPI documentation", unit = "a string") public String returnValue; diff --git a/server/src/test/java/io/opencmw/server/MajordomoWorkerTests.java b/server/src/test/java/io/opencmw/server/MajordomoWorkerTests.java index faec70dc..ce6af665 100644 --- a/server/src/test/java/io/opencmw/server/MajordomoWorkerTests.java +++ b/server/src/test/java/io/opencmw/server/MajordomoWorkerTests.java @@ -329,7 +329,7 @@ public String toString() { } } - @MetaInfo(description = "request type class description", direction = "IN") + @MetaInfo(description = "request type class description") public static class RequestDataType { @MetaInfo(description = " RequestDataType name to show up in the OpenAPI docs") public String name; @@ -367,11 +367,11 @@ public String toString() { } } - @MetaInfo(description = "reply type class description", direction = "OUT") + @MetaInfo(description = "reply type class description") public static class ReplyDataType { @MetaInfo(description = "ReplyDataType name to show up in the OpenAPI docs") public String name; - @MetaInfo(description = "a return value", unit = "A", direction = "OUT", groups = { "A", "B" }) + @MetaInfo(description = "a return value", unit = "A") public int returnValue; public ReplyDataType() { // needs default constructor