You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kp...@apache.org on 2026/08/10 21:12:18 UTC
(qpid-interop-test) 18/41: Phase 2b.4: Add .NET Proton JMS emulation
This is an automated email from the ASF dual-hosted git repository.
kpvdr pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/qpid-interop-test.git
commit 1b21705a49d5aad3dfb0a8875e629fb1cf638ac8
Author: QIT Development Team <qi...@example.com>
AuthorDate: Fri Jul 17 23:05:12 2026 -0400
Phase 2b.4: Add .NET Proton JMS emulation
Expand JMS interop test matrix from 4×4 to 5×5 (125 tests).
Changes:
- .NET shim: Add --jms-mode flag support
- .NET sender: Add JMS message type annotation via SetAnnotation()
- .NET receiver: Detect and decode JMS TextMessage via HasAnnotation()/GetAnnotation()
- Enable dotnet-proton in ENABLED_CLIENTS list
Technical details:
- Used message.SetAnnotation("x-opt-jms-msg-type", sbyte) for annotation
- Used message.HasAnnotation() and GetAnnotation() for detection
- Map AMQP string → JMS TextMessage (type 5)
- Decode JMS TextMessage as type 'text'
Test results: 125/125 tests passing locally (5×5 matrix)
Co-Authored-By: Claude Sonnet 4.5 <no...@anthropic.com>
---
shims/dotnet-proton/src/Program.cs | 8 +++--
shims/dotnet-proton/src/Receiver.cs | 64 ++++++++++++++++++++++++++++++++++++-
shims/dotnet-proton/src/Sender.cs | 31 +++++++++++++++++-
tests/test_jms_unified.py | 33 ++++++++++++++++---
4 files changed, 126 insertions(+), 10 deletions(-)
diff --git a/shims/dotnet-proton/src/Program.cs b/shims/dotnet-proton/src/Program.cs
index 9b3c5da..fffc770 100644
--- a/shims/dotnet-proton/src/Program.cs
+++ b/shims/dotnet-proton/src/Program.cs
@@ -22,25 +22,27 @@ namespace Qit.Shim
var sendTypeOption = new Option<string>("--type", "AMQP type") { IsRequired = true };
var sendCountOption = new Option<int>("--count", "Message count") { IsRequired = false };
var sendDataOption = new Option<string>("--data", "JSON test data") { IsRequired = true };
+ var sendJmsModeOption = new Option<bool>("--jms-mode", () => false, "Enable JMS emulation mode");
sendCommand.AddOption(sendBrokerOption);
sendCommand.AddOption(sendQueueOption);
sendCommand.AddOption(sendTypeOption);
sendCommand.AddOption(sendCountOption);
sendCommand.AddOption(sendDataOption);
+ sendCommand.AddOption(sendJmsModeOption);
- sendCommand.SetHandler((broker, queue, type, count, data) =>
+ sendCommand.SetHandler((broker, queue, type, count, data, jmsMode) =>
{
try
{
- Sender.Send(broker, queue, type, data);
+ Sender.Send(broker, queue, type, data, jmsMode);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
Environment.Exit(1);
}
- }, sendBrokerOption, sendQueueOption, sendTypeOption, sendCountOption, sendDataOption);
+ }, sendBrokerOption, sendQueueOption, sendTypeOption, sendCountOption, sendDataOption, sendJmsModeOption);
// Receive command
var receiveCommand = new Command("receive", "Receive AMQP messages");
diff --git a/shims/dotnet-proton/src/Receiver.cs b/shims/dotnet-proton/src/Receiver.cs
index 1e0f0e0..b541c25 100644
--- a/shims/dotnet-proton/src/Receiver.cs
+++ b/shims/dotnet-proton/src/Receiver.cs
@@ -44,7 +44,25 @@ namespace Qit.Shim
}
IMessage<object> message = delivery.Message();
- var decoded = TypeCodec.Decode(message.Body);
+
+ // Check for JMS message type annotation
+ sbyte jmsType = -1;
+ if (message.HasAnnotation("x-opt-jms-msg-type"))
+ {
+ jmsType = Convert.ToSByte(message.GetAnnotation("x-opt-jms-msg-type"));
+ }
+
+ DecodedMessage decoded;
+ if (jmsType >= 0)
+ {
+ // Decode as JMS message
+ decoded = DecodeJmsMessage(message.Body, jmsType);
+ }
+ else
+ {
+ // Decode as regular AMQP message
+ decoded = TypeCodec.Decode(message.Body);
+ }
messages.Add(new MessageResult
{
@@ -80,5 +98,49 @@ namespace Qit.Shim
var uri = new Uri(broker.StartsWith("amqp://") ? broker : $"amqp://{broker}");
return (uri.Host, uri.Port > 0 ? uri.Port : 5672);
}
+
+ private static DecodedMessage DecodeJmsMessage(object body, sbyte jmsType)
+ {
+ // JMS message type constants
+ const sbyte JMS_MESSAGE = 0;
+ const sbyte JMS_TEXT_MESSAGE = 5;
+ const sbyte JMS_BYTES_MESSAGE = 3;
+ const sbyte JMS_MAP_MESSAGE = 2;
+ const sbyte JMS_STREAM_MESSAGE = 4;
+
+ if (jmsType == JMS_TEXT_MESSAGE)
+ {
+ // TextMessage: body is string in AmqpValue section
+ return new DecodedMessage
+ {
+ Type = "text", // Use 'text' to match JMS shim output
+ Value = body as string
+ };
+ }
+ else if (jmsType == JMS_BYTES_MESSAGE)
+ {
+ // BytesMessage: body is binary in Data section
+ if (body is byte[] bytes)
+ {
+ return new DecodedMessage
+ {
+ Type = "bytes",
+ Value = BitConverter.ToString(bytes).Replace("-", "").ToLower()
+ };
+ }
+ }
+ else if (jmsType == JMS_MESSAGE)
+ {
+ // Empty message
+ return new DecodedMessage
+ {
+ Type = "null",
+ Value = null
+ };
+ }
+
+ // Unknown JMS type, fall back to regular AMQP decoding
+ return TypeCodec.Decode(body);
+ }
}
}
diff --git a/shims/dotnet-proton/src/Sender.cs b/shims/dotnet-proton/src/Sender.cs
index 19f41e6..ad9e06c 100644
--- a/shims/dotnet-proton/src/Sender.cs
+++ b/shims/dotnet-proton/src/Sender.cs
@@ -11,7 +11,7 @@ namespace Qit.Shim
{
public static class Sender
{
- public static void Send(string broker, string queue, string type, string data)
+ public static void Send(string broker, string queue, string type, string data, bool jmsMode = false)
{
try
{
@@ -40,6 +40,18 @@ namespace Qit.Shim
message.MessageId = testMsg.Index.ToString();
message.Body = TypeCodec.Encode(type, testMsg.Value);
+ // Add JMS annotations if in JMS mode
+ if (jmsMode)
+ {
+ sbyte jmsType = GetJmsMessageType(type);
+ if (jmsType >= 0)
+ {
+ // NOTE: Key MUST be symbol, value MUST be signed byte
+ // This matches Qpid JMS Client wire format
+ message.SetAnnotation("x-opt-jms-msg-type", jmsType);
+ }
+ }
+
sender.Send(message);
messages.Add(new MessageResult
@@ -71,6 +83,23 @@ namespace Qit.Shim
var uri = new Uri(broker.StartsWith("amqp://") ? broker : $"amqp://{broker}");
return (uri.Host, uri.Port > 0 ? uri.Port : 5672);
}
+
+ private static sbyte GetJmsMessageType(string amqpType)
+ {
+ // JMS message type constants (from Qpid JMS Client)
+ const sbyte JMS_MESSAGE = 0; // Empty message
+ const sbyte JMS_TEXT_MESSAGE = 5; // String/text
+ const sbyte JMS_BYTES_MESSAGE = 3; // Binary data
+
+ // Map AMQP types to JMS message types
+ return amqpType switch
+ {
+ "string" => JMS_TEXT_MESSAGE,
+ "binary" => JMS_BYTES_MESSAGE,
+ "null" => JMS_MESSAGE,
+ _ => -1 // Invalid
+ };
+ }
}
public class TestMessage
diff --git a/tests/test_jms_unified.py b/tests/test_jms_unified.py
index 7aeb097..fcf0ac5 100644
--- a/tests/test_jms_unified.py
+++ b/tests/test_jms_unified.py
@@ -54,7 +54,7 @@ ENABLED_CLIENTS = [
"jms", # Phase 2b.1 ✅
"javascript-rhea", # Phase 2b.2 ✅
"cpp-proton", # Phase 2b.3 ✅
- # "dotnet-proton", # Phase 2b.4 (future)
+ "dotnet-proton", # Phase 2b.4 ✅
# "java-protonj2", # Phase 2b.5 (future)
]
@@ -77,8 +77,8 @@ CLIENT_INFO = {
},
"dotnet-proton": {
"name": ".NET Proton",
- "shim_path": "shims/dotnet-proton/Sender.exe", # Different pattern
- "jms_mode": True, # Will support JMS emulation (Phase 2b.4)
+ "shim_path": "shims/dotnet-proton/shim.sh",
+ "jms_mode": True, # Phase 2b.4 ✅
},
"java-protonj2": {
"name": "Java ProtonJ2",
@@ -182,8 +182,21 @@ def run_sender(
]
if client_info["jms_mode"]:
cmd.append("--jms-mode")
+ elif client == "dotnet-proton":
+ # .NET sender with JMS emulation
+ cmd = [
+ str(shim_path),
+ "send",
+ "--broker", f"amqp://{broker_url}",
+ "--queue", queue,
+ "--type", "string",
+ "--count", str(len(messages)),
+ "--data", json.dumps(messages),
+ ]
+ if client_info["jms_mode"]:
+ cmd.append("--jms-mode")
else:
- # Future: .NET, Java ProtonJ2
+ # Future: Java ProtonJ2
pytest.skip(f"Sender for {client} not yet implemented")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
@@ -244,8 +257,18 @@ def run_receiver(
"--count", str(count),
"--timeout", str(timeout),
]
+ elif client == "dotnet-proton":
+ # .NET receiver (automatically detects JMS annotation)
+ cmd = [
+ str(shim_path),
+ "receive",
+ "--broker", f"amqp://{broker_url}",
+ "--queue", queue,
+ "--count", str(count),
+ "--timeout", str(timeout),
+ ]
else:
- # Future: .NET, Java ProtonJ2
+ # Future: Java ProtonJ2
pytest.skip(f"Receiver for {client} not yet implemented")
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout + 10)
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org