From f10bd8f7feba8421eb3183226d8943c67b6ea441 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Sep 2025 14:59:56 +0530 Subject: [PATCH 1/5] Implement optional close method check for BallerinaInputStream --- .../stdlib/crypto/BallerinaInputStream.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java index 6c0a5e8f..9bf5739c 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java @@ -18,6 +18,9 @@ package io.ballerina.stdlib.crypto; import io.ballerina.runtime.api.Environment; +import io.ballerina.runtime.api.types.MethodType; +import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -27,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.Objects; /** @@ -48,10 +52,19 @@ public class BallerinaInputStream extends InputStream { private final BStream ballerinaStream; private ByteBuffer buffer = null; private boolean endOfStream = false; + private boolean hasCloseMethod = true; public BallerinaInputStream(Environment environment, BStream ballerinaStream) { this.ballerinaStream = ballerinaStream; this.environment = environment; + + // Implementing a close method for a Ballerina stream is optional + // Need to check if the stream has a close method by accessing the iterator object type methods + Type iteratorType = ballerinaStream.getIteratorObj().getOriginalType(); + if (iteratorType instanceof ObjectType iteratorObjectType) { + MethodType[] methods = iteratorObjectType.getMethods(); + hasCloseMethod = Arrays.stream(methods).anyMatch(method -> method.getName().equals(BAL_STREAM_CLOSE)); + } } @Override @@ -71,6 +84,9 @@ public int read() throws IOException { @Override public void close() throws IOException { + if (!hasCloseMethod) { + return; + } Object result = callBalStreamMethod(BAL_STREAM_CLOSE); if (result instanceof BError bError) { throw new IOException((bError).getMessage()); From a130aef86b4c6430a4c3c155d968f006f4653c7a Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Sep 2025 15:17:48 +0530 Subject: [PATCH 2/5] Add a test case --- ballerina/tests/encrypt_decrypt_pgp_test.bal | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ballerina/tests/encrypt_decrypt_pgp_test.bal b/ballerina/tests/encrypt_decrypt_pgp_test.bal index 0b84d0e6..a61d5f7f 100644 --- a/ballerina/tests/encrypt_decrypt_pgp_test.bal +++ b/ballerina/tests/encrypt_decrypt_pgp_test.bal @@ -138,3 +138,19 @@ isolated function testNegativeEncryptAndDecryptStreamWithPgpInvalidPassphrase() test:assertFail("Should return a crypto Error"); } } + +@test:Config +isolated function testInputStreamWithoutCloseForPgpEncrypt() returns error? { + byte[] passphrase = "qCr3bv@5mj5n4eY".toBytes(); + string[] names = ["alice", "charlie"]; + stream inputStream = from string name in names + select name.toString().toBytes(); + stream encryptedStream = check encryptStreamAsPgp(inputStream, PGP_PUBLIC_KEY_PATH); + stream decryptStreamAsPgp = check decryptStreamFromPgp(encryptedStream, PGP_PRIVATE_KEY_PATH, passphrase); + byte[] actualBytes = []; + check from byte[] bytes in decryptStreamAsPgp + do { + actualBytes.push(...bytes); + }; + test:assertEquals(check string:fromBytes(actualBytes), "alicecharlie"); +} From 74cde0432812bb4a58c1c96383fc25820b1703fb Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Sep 2025 17:04:20 +0530 Subject: [PATCH 3/5] [Automated] Update the native jar versions --- ballerina/Dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 6488f734..22ad1b44 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -5,7 +5,7 @@ [ballerina] dependencies-toml-version = "2" -distribution-version = "2201.12.0" +distribution-version = "2201.13.0-20250924-081800-3dae8c03" [[package]] org = "ballerina" @@ -107,7 +107,7 @@ modules = [ [[package]] org = "ballerina" name = "time" -version = "2.7.0" +version = "2.8.0" dependencies = [ {org = "ballerina", name = "jballerina.java"} ] From f55978e310654acd98fd20dd66583542c9c64859 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Sep 2025 17:08:12 +0530 Subject: [PATCH 4/5] Update changelog --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index 65d9f8ef..c2787a4e 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - [Update OIDS of NIST approved post quantum algorithms](https://github.com/ballerina-platform/ballerina-library/issues/7678) - [Optimize hardcoded IV detection using semantic model reference counting](https://github.com/ballerina-platform/ballerina-library/issues/8257) +### Fixed +- [Implement optional close method check for BStream](https://github.com/ballerina-platform/ballerina-library/issues/8288) + ## [2.8.0] - 2025-02-11 ### Added From 5d66ea987a69259838d0659e66be21132b28bcaa Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Mon, 29 Sep 2025 17:13:09 +0530 Subject: [PATCH 5/5] Refactor BallerinaInputStream to determine close method presence at initialization --- .../io/ballerina/stdlib/crypto/BallerinaInputStream.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java index 9bf5739c..ec3da25e 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/BallerinaInputStream.java @@ -52,18 +52,21 @@ public class BallerinaInputStream extends InputStream { private final BStream ballerinaStream; private ByteBuffer buffer = null; private boolean endOfStream = false; - private boolean hasCloseMethod = true; + private final boolean hasCloseMethod; public BallerinaInputStream(Environment environment, BStream ballerinaStream) { this.ballerinaStream = ballerinaStream; this.environment = environment; // Implementing a close method for a Ballerina stream is optional - // Need to check if the stream has a close method by accessing the iterator object type methods + // There is no Ballerina runtime API to check if the stream has a close method + // So accessing the iterator object type methods to check if it has a close method Type iteratorType = ballerinaStream.getIteratorObj().getOriginalType(); if (iteratorType instanceof ObjectType iteratorObjectType) { MethodType[] methods = iteratorObjectType.getMethods(); hasCloseMethod = Arrays.stream(methods).anyMatch(method -> method.getName().equals(BAL_STREAM_CLOSE)); + } else { + hasCloseMethod = false; } }