Skip to content

Commit

Permalink
Show detecting incorrect write order on SBE flyweight.
Browse files Browse the repository at this point in the history
Adds a new schema and test, which is referenced from the SBE Warnings
page. The aim is to show how precedence checks in SBE `1.30.0` can help
prevent some incorrect usages of SBE flyweights.
  • Loading branch information
ZachBray committed Dec 8, 2023
1 parent 5bc3e2c commit 331eba3
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 4 deletions.
36 changes: 32 additions & 4 deletions sbe-core/src/test/java/com/aeroncookbook/sbe/SbeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

package com.aeroncookbook.sbe;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.ByteBuffer;

import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class SbeTests
{

Expand Down Expand Up @@ -175,6 +177,32 @@ public void dataCanBeCorrupted()
Assertions.assertNotEquals(DATA_2, decoder.data2());
}

@Test
public void corruptionCanBeDetected() throws NoSuchFieldException, IllegalAccessException
{
assumePrecedenceChecksAreEnabled();

final SampleCorruptionDetectedEncoder encoder = new SampleCorruptionDetectedEncoder();
final MessageHeaderEncoder messageHeaderEncoder = new MessageHeaderEncoder();
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);

encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
final IllegalStateException exception =
assertThrows(IllegalStateException.class, () -> encoder.data2(DATA_2));
final String expectedErrorFragment =
"Illegal field access order. Cannot access field \"data2\" in state: V0_BLOCK.";
Assertions.assertTrue(exception.getMessage().contains(expectedErrorFragment));
}

private static void assumePrecedenceChecksAreEnabled() throws NoSuchFieldException, IllegalAccessException
{
final Field enabledField = SampleCorruptionDetectedEncoder.class
.getDeclaredField("SBE_ENABLE_PRECEDENCE_CHECKS");
enabledField.setAccessible(true);
Assertions.assertTrue((boolean)enabledField.get(null));
}

@Test
public void dataCanBeReadCorrectlyWhenWrittenCorrectly()
{
Expand Down
18 changes: 18 additions & 0 deletions sbe-protocol/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,25 @@ tasks {
outputs.dir(generatedDir)
}

task("generateCodecsWithPrecedeceChecks", JavaExec::class) {
group = "sbe"
val codecsFile = "src/main/resources/precedence-checked-messages.xml"
val sbeFile = "src/main/resources/sbe/sbe.xsd"
inputs.files(codecsFile, sbeFile)
outputs.dir(generatedDir)
classpath = codecGeneration
mainClass.set("uk.co.real_logic.sbe.SbeTool")
args = listOf(codecsFile)
systemProperties["sbe.output.dir"] = generatedDir
systemProperties["sbe.target.language"] = "Java"
systemProperties["sbe.validation.xsd"] = sbeFile
systemProperties["sbe.validation.stop.on.error"] = "true"
systemProperties["sbe.generate.precedence.checks"] = "true"
outputs.dir(generatedDir)
}

compileJava {
dependsOn("generateCodecs")
dependsOn("generateCodecsWithPrecedeceChecks")
}
}
48 changes: 48 additions & 0 deletions sbe-protocol/src/main/resources/precedence-checked-messages.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2019-2023 Adaptive Financial Consulting Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
package="com.aeroncookbook.sbe"
id="689"
version="1"
semanticVersion="0.1"
description="Sample SBE Messages"
byteOrder="littleEndian">
<types>
<composite name="messageHeader" description="Message identifiers and length of message root">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
<composite name="varStringEncoding">
<type name="length" primitiveType="uint32" maxValue="1073741824"/>
<type name="varData" primitiveType="uint8" length="0" characterEncoding="UTF-8"/>
</composite>
</types>
<types>
<type name="Sequence" primitiveType="int64"/>
</types>

<sbe:message name="SampleCorruptionDetected" id="1002" description="Corruption sample">
<field name="sequence1" id="1" type="Sequence"/>
<field name="sequence2" id="2" type="Sequence"/>
<data name="data1" id="3" type="varStringEncoding"/>
<data name="data2" id="4" type="varStringEncoding"/>
</sbe:message>

</sbe:messageSchema>

0 comments on commit 331eba3

Please sign in to comment.