From 6f5f94f8061ca00f252c882425c3386237857608 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 11:24:31 +0530 Subject: [PATCH 01/18] Update change log --- changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index 1e5cf2e..9e61103 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- [Add Support for Retrieving Messages from IBM MQ by Matching Correlation ID and Message ID](https://github.com/ballerina-platform/ballerina-library/issues/6918) + +## [1.0.0] - 2024-07-08 + ### Changed - [Decouple IBM MQ java client jar from the IBM MQ connector](https://github.com/ballerina-platform/ballerina-library/issues/6287) From e7b26ac21dce8c5d0da6354a8f22e143a71eddbd Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 11:25:03 +0530 Subject: [PATCH 02/18] Update ballerina types related to IBM MQ GET operation with filtering support --- ballerina/types.bal | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ballerina/types.bal b/ballerina/types.bal index 2e2fb10..c5f06b8 100644 --- a/ballerina/types.bal +++ b/ballerina/types.bal @@ -83,9 +83,20 @@ public type CertKey record {| # + options - Get message option # + waitInterval - The maximum time (in seconds) that a `get` call waits for a suitable message to # arrive. It is used in conjunction with `ibmmq.MQGMO_WAIT`. +# + matchOptions - Message selection criteria public type GetMessageOptions record {| int options = MQGMO_NO_WAIT; int waitInterval = 10; + MatchOptions matchOptions?; +|}; + +# Represents the selection criteria that determine which message is retrieved. +# +# + messageId - The message identifier of the message which needs to be retrieved +# + correlationId - The Correlation identifier of the message which needs to be retrieved +public type MatchOptions record {| + byte[] messageId?; + byte[] correlationId?; |}; # Represents an IBM MQ message property. From c7562b8a409a45f2f7af04e5b780ee9b28ed7a97 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 13:58:30 +0530 Subject: [PATCH 03/18] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 02ee1f1..5bf8b6d 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerinax" name = "ibm.ibmmq" -version = "1.0.0" +version = "1.0.1" authors = ["Ballerina"] keywords = ["ibm.ibmmq", "client", "messaging", "network", "pubsub"] repository = "https://github.com/ballerina-platform/module-ballerinax-ibm.ibmmq" @@ -12,8 +12,8 @@ distribution = "2201.9.0" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "ibm.ibmmq-native" -version = "1.0.0" -path = "../native/build/libs/ibm.ibmmq-native-1.0.0.jar" +version = "1.0.1" +path = "../native/build/libs/ibm.ibmmq-native-1.0.1-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "org.json" From d45bd1863a0a11b75c14dc522dcf3f8007dc6a12 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:06:16 +0530 Subject: [PATCH 04/18] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index cabef97..8dcc15b 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -90,7 +90,7 @@ modules = [ [[package]] org = "ballerinax" name = "ibm.ibmmq" -version = "1.0.0" +version = "1.0.1" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, From 8d012f60303cd59124740125e58cedcf6c391a54 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:06:46 +0530 Subject: [PATCH 05/18] Update spotbugs excludes --- spotbugs-exclude.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml index 0151992..ae4f7c8 100644 --- a/spotbugs-exclude.xml +++ b/spotbugs-exclude.xml @@ -24,10 +24,22 @@ + + + + + + + + + + + + From b26114c758b488c66ccaa97edb8f1aa9110885af Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:13:06 +0530 Subject: [PATCH 06/18] Add support to retrieve messages based on msg-id or correlation-id --- .../ballerina/lib/ibm.ibmmq/CommonUtils.java | 44 +++++++++++---- .../io/ballerina/lib/ibm.ibmmq/Constants.java | 1 + .../io/ballerina/lib/ibm.ibmmq/Queue.java | 12 +++-- .../io/ballerina/lib/ibm.ibmmq/Topic.java | 12 +++-- .../ibm.ibmmq/config/GetMessageOptions.java | 53 +++++++++++++++++++ .../lib/ibm.ibmmq/config/MatchOptions.java | 48 +++++++++++++++++ 6 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java create mode 100644 native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java index d3957f9..504b8fd 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java @@ -22,7 +22,10 @@ import com.ibm.mq.MQGetMessageOptions; import com.ibm.mq.MQMessage; import com.ibm.mq.MQPropertyDescriptor; +import com.ibm.mq.constants.MQConstants; import com.ibm.mq.headers.MQHeaderList; +import io.ballerina.lib.ibm.ibmmq.config.GetMessageOptions; +import io.ballerina.lib.ibm.ibmmq.config.MatchOptions; import io.ballerina.lib.ibm.ibmmq.headers.MQRFH2Header; import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.Runtime; @@ -66,7 +69,6 @@ import static io.ballerina.lib.ibm.ibmmq.Constants.MESSAGE_PROPERTY; import static io.ballerina.lib.ibm.ibmmq.Constants.MESSAGE_PROPERTIES; import static io.ballerina.lib.ibm.ibmmq.Constants.MESSAGE_TYPE_FIELD; -import static io.ballerina.lib.ibm.ibmmq.Constants.OPTIONS; import static io.ballerina.lib.ibm.ibmmq.Constants.PD_CONTEXT; import static io.ballerina.lib.ibm.ibmmq.Constants.PD_COPY_OPTIONS; import static io.ballerina.lib.ibm.ibmmq.Constants.PD_OPTIONS; @@ -79,7 +81,6 @@ import static io.ballerina.lib.ibm.ibmmq.Constants.PUT_APPLICATION_TYPE_FIELD; import static io.ballerina.lib.ibm.ibmmq.Constants.REPLY_TO_QM_NAME_FIELD; import static io.ballerina.lib.ibm.ibmmq.Constants.REPLY_TO_QUEUE_NAME_FIELD; -import static io.ballerina.lib.ibm.ibmmq.Constants.WAIT_INTERVAL; import static io.ballerina.lib.ibm.ibmmq.ModuleUtils.getModule; import static io.ballerina.lib.ibm.ibmmq.headers.MQCIHHeader.createMQCIHHeaderFromBHeader; import static io.ballerina.lib.ibm.ibmmq.headers.MQIIHHeader.createMQIIHHeaderFromBHeader; @@ -293,13 +294,38 @@ private static BMap populateDescriptorFromMQPropertyDescriptor(MQPropertyDescrip return descriptor; } - public static MQGetMessageOptions getGetMessageOptions(BMap bOptions) { - int waitInterval = bOptions.getIntValue(WAIT_INTERVAL).intValue(); - int options = bOptions.getIntValue(OPTIONS).intValue(); - MQGetMessageOptions getMessageOptions = new MQGetMessageOptions(); - getMessageOptions.waitInterval = waitInterval * 1000; - getMessageOptions.options = options; - return getMessageOptions; + public static MQMessage getMqMessage(MatchOptions matchOptions) { + MQMessage message = new MQMessage(); + if (Objects.isNull(matchOptions)) { + return message; + } + + if (Objects.nonNull(matchOptions.messageId())) { + message.messageId = matchOptions.messageId(); + } + if (Objects.nonNull(matchOptions.correlationId())) { + message.correlationId = matchOptions.correlationId(); + } + return message; + } + + public static MQGetMessageOptions getMqGetMsgOptions(GetMessageOptions getMsgOptions) { + MQGetMessageOptions mqGetMsgOptions = new MQGetMessageOptions(); + mqGetMsgOptions.waitInterval = getMsgOptions.waitInterval(); + mqGetMsgOptions.options = getMsgOptions.options(); + + MatchOptions matchOptions = getMsgOptions.matchOptions(); + if (Objects.isNull(matchOptions)) { + return mqGetMsgOptions; + } + + if (Objects.nonNull(matchOptions.messageId())) { + mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID; + } + if (Objects.nonNull(matchOptions.correlationId())) { + mqGetMsgOptions.matchOptions |= MQConstants.MQMO_MATCH_CORREL_ID; + } + return mqGetMsgOptions; } private static Object getBHeaders(Runtime runtime, MQMessage mqMessage) { diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Constants.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Constants.java index 60d9551..91d3b3b 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Constants.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Constants.java @@ -108,6 +108,7 @@ public interface Constants { BString PROPERTY_DESCRIPTOR = StringUtils.fromString("descriptor"); BString WAIT_INTERVAL = StringUtils.fromString("waitInterval"); BString OPTIONS = StringUtils.fromString("options"); + BString MATCH_OPTIONS = StringUtils.fromString("matchOptions"); BString FORMAT_FIELD = StringUtils.fromString("format"); BString MESSAGE_ID_FIELD = StringUtils.fromString("messageId"); BString CORRELATION_ID_FIELD = StringUtils.fromString("correlationId"); diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Queue.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Queue.java index a723fdc..5b314ed 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Queue.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Queue.java @@ -23,6 +23,7 @@ import com.ibm.mq.MQMessage; import com.ibm.mq.MQQueue; import com.ibm.mq.constants.CMQC; +import io.ballerina.lib.ibm.ibmmq.config.GetMessageOptions; import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Future; import io.ballerina.runtime.api.values.BError; @@ -60,15 +61,16 @@ public static Object put(Environment environment, BObject queueObject, BMap options) { + public static Object get(Environment environment, BObject queueObject, BMap bGetMsgOptions) { MQQueue queue = (MQQueue) queueObject.getNativeData(Constants.NATIVE_QUEUE); - MQGetMessageOptions getMessageOptions = CommonUtils.getGetMessageOptions(options); + GetMessageOptions getMsgOptions = new GetMessageOptions(bGetMsgOptions); + MQMessage mqMessage = CommonUtils.getMqMessage(getMsgOptions.matchOptions()); + MQGetMessageOptions mqGetMsgOptions = CommonUtils.getMqGetMsgOptions(getMsgOptions); Future future = environment.markAsync(); QUEUE_EXECUTOR_SERVICE.execute(() -> { try { - MQMessage message = new MQMessage(); - queue.get(message, getMessageOptions); - future.complete(CommonUtils.getBMessageFromMQMessage(environment.getRuntime(), message)); + queue.get(mqMessage, mqGetMsgOptions); + future.complete(CommonUtils.getBMessageFromMQMessage(environment.getRuntime(), mqMessage)); } catch (MQException e) { if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) { future.complete(null); diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Topic.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Topic.java index fc6272b..1d5611d 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Topic.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/Topic.java @@ -23,6 +23,7 @@ import com.ibm.mq.MQMessage; import com.ibm.mq.MQTopic; import com.ibm.mq.constants.CMQC; +import io.ballerina.lib.ibm.ibmmq.config.GetMessageOptions; import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Future; import io.ballerina.runtime.api.values.BError; @@ -60,15 +61,16 @@ public static Object put(Environment environment, BObject topicObject, BMap mess return null; } - public static Object get(Environment environment, BObject topicObject, BMap options) { + public static Object get(Environment environment, BObject topicObject, BMap bGetMsgOptions) { MQTopic topic = (MQTopic) topicObject.getNativeData(Constants.NATIVE_TOPIC); - MQGetMessageOptions getMessageOptions = CommonUtils.getGetMessageOptions(options); + GetMessageOptions getMsgOptions = new GetMessageOptions(bGetMsgOptions); + MQMessage mqMessage = CommonUtils.getMqMessage(getMsgOptions.matchOptions()); + MQGetMessageOptions mqGetMsgOptions = CommonUtils.getMqGetMsgOptions(getMsgOptions); Future future = environment.markAsync(); topicExecutorService.execute(() -> { try { - MQMessage message = new MQMessage(); - topic.get(message, getMessageOptions); - future.complete(CommonUtils.getBMessageFromMQMessage(environment.getRuntime(), message)); + topic.get(mqMessage, mqGetMsgOptions); + future.complete(CommonUtils.getBMessageFromMQMessage(environment.getRuntime(), mqMessage)); } catch (MQException e) { if (e.reasonCode == CMQC.MQRC_NO_MSG_AVAILABLE) { future.complete(null); diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java new file mode 100644 index 0000000..b8ab9a1 --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you 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 + * + * http://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. + */ + +package io.ballerina.lib.ibm.ibmmq.config; + +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import static io.ballerina.lib.ibm.ibmmq.Constants.MATCH_OPTIONS; +import static io.ballerina.lib.ibm.ibmmq.Constants.OPTIONS; +import static io.ballerina.lib.ibm.ibmmq.Constants.WAIT_INTERVAL; + +/** + * Represents the IBM MQ GET message options. + * + * @param options Get message option + * @param waitInterval The maximum time (in seconds) that a `get` call waits for a suitable message to arrive. + * It is used in conjunction with `MQGMO_WAIT`. + * @param matchOptions Message selection criteria + */ +public record GetMessageOptions(int options, int waitInterval, MatchOptions matchOptions) { + + public GetMessageOptions(BMap getMsgOptions) { + this ( + getMsgOptions.getIntValue(OPTIONS).intValue(), + getMsgOptions.getIntValue(WAIT_INTERVAL).intValue() * 1000, + getMatchOptions(getMsgOptions) + ); + } + + @SuppressWarnings("unchecked") + private static MatchOptions getMatchOptions(BMap getMsgOptions) { + if (!getMsgOptions.containsKey(MATCH_OPTIONS)) { + return null; + } + return new MatchOptions((BMap) getMsgOptions.getMapValue(MATCH_OPTIONS)); + } +} diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java new file mode 100644 index 0000000..e0f418a --- /dev/null +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you 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 + * + * http://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. + */ + +package io.ballerina.lib.ibm.ibmmq.config; + +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; + +import static io.ballerina.lib.ibm.ibmmq.Constants.CORRELATION_ID_FIELD; +import static io.ballerina.lib.ibm.ibmmq.Constants.MESSAGE_ID_FIELD; + +/** + * Represents the selection criteria that determine which message is retrieved. + * + * @param messageId The message identifier of the message which needs to be retrieved + * @param correlationId The Correlation identifier of the message which needs to be retrieved + */ +public record MatchOptions(byte[] messageId, byte[] correlationId) { + + public MatchOptions(BMap matchOptions) { + this ( + getValueIfPresent(matchOptions, MESSAGE_ID_FIELD), + getValueIfPresent(matchOptions, CORRELATION_ID_FIELD) + ); + } + + private static byte[] getValueIfPresent(BMap matchOptions, BString key) { + if (!matchOptions.containsKey(key)) { + return null; + } + return matchOptions.getArrayValue(key).getByteArray(); + } +} From 20e7c2be5286baf5074076860ebe8a37036cf1b9 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:18:48 +0530 Subject: [PATCH 07/18] Restructure the match-options identifying logic --- .../java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java index 504b8fd..55b4e27 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java @@ -319,11 +319,12 @@ public static MQGetMessageOptions getMqGetMsgOptions(GetMessageOptions getMsgOpt return mqGetMsgOptions; } - if (Objects.nonNull(matchOptions.messageId())) { + if (Objects.nonNull(matchOptions.messageId()) && Objects.nonNull(matchOptions.correlationId())) { + mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID | MQConstants.MQMO_MATCH_CORREL_ID; + } else if (Objects.nonNull(matchOptions.messageId())) { mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID; - } - if (Objects.nonNull(matchOptions.correlationId())) { - mqGetMsgOptions.matchOptions |= MQConstants.MQMO_MATCH_CORREL_ID; + } else if (Objects.nonNull(matchOptions.correlationId())){ + mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_CORREL_ID; } return mqGetMsgOptions; } From 435b42becb9b8d52a2d50f83b8d4b179f94656d4 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:20:27 +0530 Subject: [PATCH 08/18] Refactor code base --- .../io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java index e0f418a..dcc2a1b 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java @@ -34,12 +34,12 @@ public record MatchOptions(byte[] messageId, byte[] correlationId) { public MatchOptions(BMap matchOptions) { this ( - getValueIfPresent(matchOptions, MESSAGE_ID_FIELD), - getValueIfPresent(matchOptions, CORRELATION_ID_FIELD) + getByteArrIfPresent(matchOptions, MESSAGE_ID_FIELD), + getByteArrIfPresent(matchOptions, CORRELATION_ID_FIELD) ); } - private static byte[] getValueIfPresent(BMap matchOptions, BString key) { + private static byte[] getByteArrIfPresent(BMap matchOptions, BString key) { if (!matchOptions.containsKey(key)) { return null; } From 43ffb715b7c7d1d90bb9634484be24079adade08 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 14:23:55 +0530 Subject: [PATCH 09/18] Reformat the code --- .../src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java | 2 +- .../io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java | 4 ++-- .../java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java index 55b4e27..22ccb3d 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java @@ -323,7 +323,7 @@ public static MQGetMessageOptions getMqGetMsgOptions(GetMessageOptions getMsgOpt mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID | MQConstants.MQMO_MATCH_CORREL_ID; } else if (Objects.nonNull(matchOptions.messageId())) { mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID; - } else if (Objects.nonNull(matchOptions.correlationId())){ + } else if (Objects.nonNull(matchOptions.correlationId())) { mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_CORREL_ID; } return mqGetMsgOptions; diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java index b8ab9a1..338c867 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/GetMessageOptions.java @@ -28,7 +28,7 @@ /** * Represents the IBM MQ GET message options. * - * @param options Get message option + * @param options Get message option * @param waitInterval The maximum time (in seconds) that a `get` call waits for a suitable message to arrive. * It is used in conjunction with `MQGMO_WAIT`. * @param matchOptions Message selection criteria @@ -36,7 +36,7 @@ public record GetMessageOptions(int options, int waitInterval, MatchOptions matchOptions) { public GetMessageOptions(BMap getMsgOptions) { - this ( + this( getMsgOptions.getIntValue(OPTIONS).intValue(), getMsgOptions.getIntValue(WAIT_INTERVAL).intValue() * 1000, getMatchOptions(getMsgOptions) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java index dcc2a1b..1ead78e 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/config/MatchOptions.java @@ -27,13 +27,13 @@ /** * Represents the selection criteria that determine which message is retrieved. * - * @param messageId The message identifier of the message which needs to be retrieved + * @param messageId The message identifier of the message which needs to be retrieved * @param correlationId The Correlation identifier of the message which needs to be retrieved */ public record MatchOptions(byte[] messageId, byte[] correlationId) { public MatchOptions(BMap matchOptions) { - this ( + this( getByteArrIfPresent(matchOptions, MESSAGE_ID_FIELD), getByteArrIfPresent(matchOptions, CORRELATION_ID_FIELD) ); From 3a43195ea97416ac2b33e525be4fd73fc0769c0f Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 16:56:48 +0530 Subject: [PATCH 10/18] Add test cases for IBM MQ queue usecases --- ballerina/build.gradle | 13 ++- .../tests/queue_producer_consumer_tests.bal | 97 +++++++++++++++++++ 2 files changed, 105 insertions(+), 5 deletions(-) diff --git a/ballerina/build.gradle b/ballerina/build.gradle index 22b9d8b..3a5094f 100644 --- a/ballerina/build.gradle +++ b/ballerina/build.gradle @@ -180,10 +180,13 @@ publishing { updateTomlFiles.dependsOn copyStdlibs - test.dependsOn startIBMMQServer - build.finalizedBy stopIBMMQServer +build.dependsOn ":${packageName}-native:build" +build.dependsOn startIBMMQServer +build.finalizedBy stopIBMMQServer + +test.dependsOn ":${packageName}-native:build" +test.dependsOn startIBMMQServer +test.finalizedBy stopIBMMQServer -build.dependsOn ":ibm.ibmmq-native:build" -build.dependsOn "generatePomFileForMavenPublication" -publishToMavenLocal.dependsOn build publish.dependsOn build +publishToMavenLocal.dependsOn build diff --git a/ballerina/tests/queue_producer_consumer_tests.bal b/ballerina/tests/queue_producer_consumer_tests.bal index 1a15fd4..eb5294a 100644 --- a/ballerina/tests/queue_producer_consumer_tests.bal +++ b/ballerina/tests/queue_producer_consumer_tests.bal @@ -518,3 +518,100 @@ function produceAndConsumerMessageWithMultipleHeaderTypesWithJsonPayloadTest() r check queueManager.disconnect(); } +@test:Config { + groups: ["ibmmqQueue", "matchOptions"] +} +function produceConsumeWithMsgId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + byte[] providedMsgId = "msg-id-1".toBytes(); + string messageContent = "This is a sample message with a message-id."; + check queue->put({ + messageId: providedMsgId, + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { messageId: providedMsgId }); + test:assertTrue(message is Message, "Could not retrieve a message for a valid message identifier"); + + byte[]? payload = message?.payload; + test:assertEquals(string:fromBytes(check payload.ensureType()), messageContent); + + check queue->close(); + check queueManager.disconnect(); +} + +@test:Config { + groups: ["ibmmqQueue", "matchOptions"] +} +function produceConsumeWithInvalidMsgId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + string messageContent = "This is a sample message with a message-id."; + check queue->put({ + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { messageId: "test-msg-id-1".toBytes() }); + test:assertTrue(message is (), "Retrieved a message for an invalid message identifier"); + + check queue->close(); + check queueManager.disconnect(); +} + +@test:Config { + groups: ["ibmmqQueue", "matchOptions"] +} +function produceConsumeWithCorrId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + byte[] providedCorrId = "msg-id-1".toBytes(); + string messageContent = "This is a sample message with a message-id."; + check queue->put({ + correlationId: providedCorrId, + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { correlationId: providedCorrId }); + test:assertTrue(message is Message, "Could not retrieve a message for a valid correlation identifier"); + + byte[]? correlationId = message?.correlationId; + test:assertTrue(correlationId is byte[], "Could not find the correlation identifier for the message"); + + byte[]? payload = message?.payload; + test:assertEquals(string:fromBytes(check payload.ensureType()), messageContent); + + check queue->close(); + check queueManager.disconnect(); +} + +@test:Config { + groups: ["ibmmqQueue", "matchOptions"] +} +function produceConsumeWithInvalidCorrId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + string messageContent = "This is a sample message with a message-id."; + check queue->put({ + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { correlationId: "test-msg-id-1".toBytes() }); + test:assertTrue(message is (), "Retrieved a message for an invalid correlation identifier"); + + check queue->close(); + check queueManager.disconnect(); +} + From 7a328b77dd436fa95da8cc7a2d4ee9817a7196e7 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 16:57:16 +0530 Subject: [PATCH 11/18] Update readme with package test instruction with test-groups --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fb69722..7fdf85d 100644 --- a/README.md +++ b/README.md @@ -205,19 +205,23 @@ Execute the commands below to build from the source. ``` ./gradlew clean build -Pdebug= ``` -5. To debug the library with Ballerina language: +5. To run a group of tests: + ``` + ./gradlew clean test -Pgroups= + ``` +6. To debug the library with Ballerina language: ``` ./gradlew clean build -PbalJavaDebug= ``` -6. Publish ZIP artifact to the local `.m2` repository: +7. Publish ZIP artifact to the local `.m2` repository: ``` ./gradlew clean build publishToMavenLocal ``` -7. Publish the generated artifacts to the local Ballerina central repository: +8. Publish the generated artifacts to the local Ballerina central repository: ``` ./gradlew clean build -PpublishToLocalCentral=true ``` -8. Publish the generated artifacts to the Ballerina central repository: +9. Publish the generated artifacts to the Ballerina central repository: ``` ./gradlew clean build -PpublishToCentral=true ``` From d77fb729266f35f0d7fd498af0c4c7895d60b348 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 17:05:37 +0530 Subject: [PATCH 12/18] Restructure queue related test cases --- .../tests/queue_producer_consumer_tests.bal | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/ballerina/tests/queue_producer_consumer_tests.bal b/ballerina/tests/queue_producer_consumer_tests.bal index eb5294a..09bce20 100644 --- a/ballerina/tests/queue_producer_consumer_tests.bal +++ b/ballerina/tests/queue_producer_consumer_tests.bal @@ -574,8 +574,8 @@ function produceConsumeWithCorrId() returns error? { userID = "app", password = "password"); Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); - byte[] providedCorrId = "msg-id-1".toBytes(); - string messageContent = "This is a sample message with a message-id."; + byte[] providedCorrId = "corr-id-1".toBytes(); + string messageContent = "This is a sample message with a correlation-id."; check queue->put({ correlationId: providedCorrId, payload: messageContent.toBytes() @@ -608,10 +608,88 @@ function produceConsumeWithInvalidCorrId() returns error? { payload: messageContent.toBytes() }); - Message? message = check queue->get(matchOptions = { correlationId: "test-msg-id-1".toBytes() }); + Message? message = check queue->get(matchOptions = { correlationId: "test-corr-id-1".toBytes() }); test:assertTrue(message is (), "Retrieved a message for an invalid correlation identifier"); check queue->close(); check queueManager.disconnect(); } +@test:Config { + groups: ["ibmmqQueue", "matchOptions"] +} +function produceConsumeWithMsgIdAndCorrId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + byte[] providedMsgId = "msg-id-1".toBytes(); + byte[] providedCorrId = "corr-id-1".toBytes(); + string messageContent = "This is a sample message with a message-id and a correlation-id."; + check queue->put({ + messageId: providedMsgId, + correlationId: providedCorrId, + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { messageId: providedMsgId, correlationId: providedCorrId }); + test:assertTrue(message is Message, "Could not retrieve a message for a valid message identifier and correlation identifier"); + + byte[]? payload = message?.payload; + test:assertEquals(string:fromBytes(check payload.ensureType()), messageContent); + + check queue->close(); + check queueManager.disconnect(); +} + +@test:Config { + groups: ["ibmmqQueue", "matchOptions"], + dependsOn: [produceConsumeWithMsgIdAndCorrId] +} +function produceConsumeWithInvalidMsgIdAndCorrId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + byte[] providedMsgId = "msg-id-1".toBytes(); + byte[] providedCorrId = "corr-id-1".toBytes(); + string messageContent = "This is a sample message with a message-id and a correlation-id."; + check queue->put({ + correlationId: providedCorrId, + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { messageId: providedMsgId, correlationId: providedCorrId }); + test:assertTrue(message is (), "Retrieved a message for an invalid message-id and a correct correlation identifier"); + + check queue->close(); + check queueManager.disconnect(); +} + +@test:Config { + groups: ["ibmmqQueue", "matchOptions"], + dependsOn: [produceConsumeWithInvalidMsgIdAndCorrId] +} +function produceConsumeWithMsgIdAndInvalidCorrId() returns error? { + QueueManager queueManager = check new ( + name = "QM1", host = "localhost", channel = "DEV.APP.SVRCONN", + userID = "app", password = "password"); + Queue queue = check queueManager.accessQueue("DEV.QUEUE.2", MQOO_OUTPUT | MQOO_INPUT_AS_Q_DEF); + + byte[] providedMsgId = "msg-id-1".toBytes(); + byte[] providedCorrId = "corr-id-1".toBytes(); + string messageContent = "This is a sample message with a message-id and a correlation-id."; + check queue->put({ + messageId: providedMsgId, + payload: messageContent.toBytes() + }); + + Message? message = check queue->get(matchOptions = { messageId: providedMsgId, correlationId: providedCorrId }); + test:assertTrue(message is (), "Retrieved a message for a correct message-id and an invalid correlation identifier"); + + check queue->close(); + check queueManager.disconnect(); +} + From a35bc2cb13807e19ea0acd5516c0d913918521ac Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 17:34:41 +0530 Subject: [PATCH 13/18] Restructure match-option setting logic --- .../io/ballerina/lib/ibm.ibmmq/CommonUtils.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java index 22ccb3d..475441f 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java @@ -319,13 +319,14 @@ public static MQGetMessageOptions getMqGetMsgOptions(GetMessageOptions getMsgOpt return mqGetMsgOptions; } - if (Objects.nonNull(matchOptions.messageId()) && Objects.nonNull(matchOptions.correlationId())) { - mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID | MQConstants.MQMO_MATCH_CORREL_ID; - } else if (Objects.nonNull(matchOptions.messageId())) { - mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_MSG_ID; - } else if (Objects.nonNull(matchOptions.correlationId())) { - mqGetMsgOptions.matchOptions = MQConstants.MQMO_MATCH_CORREL_ID; + int matchOpt = 0; + if (Objects.nonNull(matchOptions.messageId())) { + matchOpt |= MQConstants.MQMO_MATCH_MSG_ID; + } + if (Objects.nonNull(matchOptions.correlationId())) { + matchOpt |= MQConstants.MQMO_MATCH_CORREL_ID; } + mqGetMsgOptions.matchOptions = matchOpt; return mqGetMsgOptions; } From 61f95bca55ad435b8e58e264a8f1b81ee8fadb03 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 17:38:42 +0530 Subject: [PATCH 14/18] Update initial value to the match-option --- .../src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java index 475441f..c49e6d8 100644 --- a/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java +++ b/native/src/main/java/io/ballerina/lib/ibm.ibmmq/CommonUtils.java @@ -319,7 +319,7 @@ public static MQGetMessageOptions getMqGetMsgOptions(GetMessageOptions getMsgOpt return mqGetMsgOptions; } - int matchOpt = 0; + int matchOpt = MQConstants.MQMO_NONE; if (Objects.nonNull(matchOptions.messageId())) { matchOpt |= MQConstants.MQMO_MATCH_MSG_ID; } From f3aeedc608cf279d5c7518cdc7c18597f9039f3f Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 17:44:32 +0530 Subject: [PATCH 15/18] Update IBM MQ documentation --- docs/spec/spec.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 172c308..22e5d29 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -408,7 +408,7 @@ public type Message record {| ## 4. Client Options -- GetMessageOptions record represents client options which can be used when retrieving messages from an IBM MQ destination. +- `GetMessageOptions` record represents client options which can be used when retrieving messages from an IBM MQ destination. ```ballerina public type GetMessageOptions record {| @@ -416,6 +416,19 @@ public type GetMessageOptions record {| int options = MQGMO_NO_WAIT; # The maximum time (in seconds) that a `get` call waits for a suitable message to arrive. It is used in conjunction with `ibmmq.MQGMO_WAIT`. int waitInterval = 10; + # Message selection criteria + MatchOptions matchOptions?; +|}; +``` + +- `MatchOptions` record represents the selection criteria that determine which message is retrieved. + +```ballerina +public type MatchOptions record {| + # The message identifier of the message which needs to be retrieved + byte[] messageId?; + # The Correlation identifier of the message which needs to be retrieved + byte[] correlationId?; |}; ``` From 287e145af28774438363fb78be7e6254dea15481 Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 20:37:41 +0530 Subject: [PATCH 16/18] Update package version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ef89c59..55ae950 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.caching=true group=io.ballerina.lib -version=1.0.1-SNAPSHOT +version=1.1.0-SNAPSHOT ballerinaLangVersion=2201.9.0 checkstylePluginVersion=10.12.1 From 17f0636a5d3c9a9bd51730c29c8be73c60c7af2b Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Tue, 27 Aug 2024 20:39:58 +0530 Subject: [PATCH 17/18] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/Dependencies.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 5bf8b6d..1a236d0 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerinax" name = "ibm.ibmmq" -version = "1.0.1" +version = "1.1.0" authors = ["Ballerina"] keywords = ["ibm.ibmmq", "client", "messaging", "network", "pubsub"] repository = "https://github.com/ballerina-platform/module-ballerinax-ibm.ibmmq" @@ -12,8 +12,8 @@ distribution = "2201.9.0" [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "ibm.ibmmq-native" -version = "1.0.1" -path = "../native/build/libs/ibm.ibmmq-native-1.0.1-SNAPSHOT.jar" +version = "1.1.0" +path = "../native/build/libs/ibm.ibmmq-native-1.1.0-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "org.json" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 8dcc15b..a198d03 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -90,7 +90,7 @@ modules = [ [[package]] org = "ballerinax" name = "ibm.ibmmq" -version = "1.0.1" +version = "1.1.0" dependencies = [ {org = "ballerina", name = "crypto"}, {org = "ballerina", name = "jballerina.java"}, From 10adab0d890c553e77cef8f3bfdc79bf3e024aad Mon Sep 17 00:00:00 2001 From: ayeshLK Date: Wed, 28 Aug 2024 10:17:22 +0530 Subject: [PATCH 18/18] Fix build failure with the m2 publish --- ballerina/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/ballerina/build.gradle b/ballerina/build.gradle index 3a5094f..e197f43 100644 --- a/ballerina/build.gradle +++ b/ballerina/build.gradle @@ -180,6 +180,7 @@ publishing { updateTomlFiles.dependsOn copyStdlibs +build.dependsOn "generatePomFileForMavenPublication" build.dependsOn ":${packageName}-native:build" build.dependsOn startIBMMQServer build.finalizedBy stopIBMMQServer