Skip to content

Commit

Permalink
Refactor env var handling for Java 17
Browse files Browse the repository at this point in the history
  • Loading branch information
warunalakshitha committed Jul 27, 2023
1 parent 1a4ecf9 commit d4f8c29
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 96 deletions.
22 changes: 5 additions & 17 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "os"
version = "1.7.0"
version = "1.7.1"
authors = ["Ballerina"]
keywords = ["environment"]
repository = "https://github.com/ballerina-platform/module-ballerina-os"
Expand All @@ -12,23 +12,11 @@ distribution = "2201.7.0"
[platform.java17]
graalvmCompatible = true

[[platform.java17.dependency]]
groupId = "org.burningwave"
artifactId = "core"
version = "12.62.7"
path = "./lib/core-12.62.7.jar"

[[platform.java17.dependency]]
groupId = "io.github.toolfactory"
artifactId = "jvm-driver"
version = "9.4.3"
path = "./lib/jvm-driver-9.4.3.jar"

[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "os-native"
version = "1.7.0"
path = "../native/build/libs/os-native-1.7.0.jar"
version = "1.7.1"
path = "../native/build/libs/os-native-1.7.1-SNAPSHOT.jar"

[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
Expand All @@ -39,5 +27,5 @@ path = "./lib/io-native-1.5.0.jar"
[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "os-test-utils"
version = "1.7.0"
path = "../test-utils/build/libs/os-test-utils-1.7.0.jar"
version = "1.7.1"
path = "../test-utils/build/libs/os-test-utils-1.7.1-SNAPSHOT.jar"
4 changes: 2 additions & 2 deletions ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[ballerina]
dependencies-toml-version = "2"
distribution-version = "2201.7.0"
distribution-version = "2201.8.0-20230726-145300-b2bdf796"

[[package]]
org = "ballerina"
Expand Down Expand Up @@ -47,7 +47,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "os"
version = "1.7.0"
version = "1.7.1"
dependencies = [
{org = "ballerina", name = "io"},
{org = "ballerina", name = "jballerina.java"},
Expand Down
13 changes: 4 additions & 9 deletions ballerina/os.bal
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,12 @@ public type EnvProperties record {|
# + name - Name of the environment variable
# + return - Environment variable value if it exists or else, an empty string
public isolated function getEnv(string name) returns string {
var value = java:toString(nativeGetEnv(java:fromString(name)));
if value is string {
return value;
}
return "";
return nativeGetEnv(name);
}

isolated function nativeGetEnv(handle key) returns handle = @java:Method {
name: "getenv",
'class: "java.lang.System",
paramTypes: ["java.lang.String"]
isolated function nativeGetEnv(string key) returns string = @java:Method {
name: "getEnv",
'class: "io.ballerina.stdlib.os.nativeimpl.GetEnv"
} external;

# Returns the current user's name.
Expand Down
2 changes: 1 addition & 1 deletion ballerina/tests/Config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[os]
bal_exec_path = "@exec.path@"
bal_exec_path = "/Users/waruna/Dev/jdk17/module-ballerina-system/target/ballerina-runtime/bin/bal"
9 changes: 1 addition & 8 deletions ballerina/tests/os_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ import ballerina/io;

configurable string bal_exec_path = ?;

@test:Config {
enable:false
}
function testGetEnv() {
string expectedValue = getExpectedValidEnv();
test:assertEquals(getEnv("JAVA_HOME"), expectedValue);
Expand Down Expand Up @@ -53,7 +50,6 @@ function setEnvDataProvider() returns (string[][]) {
}

@test:Config {
enable: false,
dataProvider: setEnvDataProvider
}
function testSetEnv(string key, string value) {
Expand Down Expand Up @@ -94,7 +90,6 @@ function unsetEnvDataProvider() returns (string[][]) {
}

@test:Config {
enable: false,
dataProvider: unsetEnvDataProvider
}
function testUnsetEnv(string key, string value) {
Expand Down Expand Up @@ -212,9 +207,7 @@ function testExecWithOutputStdErr() returns error? {
test:assertTrue(stdErrString.includes("hello world"));
}

@test:Config {
enable: false
}
@test:Config {}
function testExecWithEnvironmentVariable() returns error? {
Process process = check exec({value: bal_exec_path, arguments: ["run", "tests/resources/hello3.bal"]}, BAL_CONFIG_FILES = "tests/resources/config/Config.toml");
int exitCode = check process.waitForExit();
Expand Down
4 changes: 2 additions & 2 deletions ballerina/tests/resources/hello3.bal
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
// specific language governing permissions and limitations
// under the License.

import ballerina/log;
import ballerina/io;

public function main() {
log:printDebug("debug message");
io:println("debug message");
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ researchgateReleaseVersion=2.8.0
puppycrawlCheckstyleVersion=10.12.0


ballerinaLangVersion=2201.8.0-20230726-132400-5c2e50ed
ballerinaLangVersion=2201.8.0-20230726-145300-b2bdf796
stdlibIoVersion=1.5.0
64 changes: 64 additions & 0 deletions native/src/main/java/io/ballerina/stdlib/os/nativeimpl/GetEnv.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.stdlib.os.nativeimpl;

import io.ballerina.runtime.api.Environment;
import io.ballerina.runtime.api.PredefinedTypes;
import io.ballerina.runtime.api.creators.TypeCreator;
import io.ballerina.runtime.api.creators.ValueCreator;
import io.ballerina.runtime.api.types.MapType;
import io.ballerina.runtime.api.utils.StringUtils;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;

import java.util.Map;

import static io.ballerina.stdlib.os.utils.OSConstants.ENV_VAR_KEY;

/**
* Extern function of ballerina.os:getEnv.
*
* @since 1.5.1
*/
public class GetEnv {

private GetEnv() {

}

public static BString getEnv(Environment env, BString key) {
Object envVarMap = env.getStrandLocal(ENV_VAR_KEY);
BMap<BString, Object> envMap;
if (envVarMap == null) {
MapType mapType = TypeCreator.createMapType(PredefinedTypes.TYPE_STRING);
envMap = ValueCreator.createMapValue(mapType);
Map<String, String> jEnvMap = System.getenv();
for (Map.Entry<String, String> entry : jEnvMap.entrySet()) {
envMap.put(StringUtils.fromString(entry.getKey()), StringUtils.fromString(entry.getValue()));
}
env.setStrandLocal(ENV_VAR_KEY, envMap);
} else {
envMap = (BMap<BString, Object>) envVarMap;
}
Object value = envMap.get(key);
if (value == null) {
return StringUtils.fromString("");
}
return (BString) value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package io.ballerina.stdlib.os.nativeimpl;

import io.ballerina.runtime.api.Environment;
import io.ballerina.runtime.api.PredefinedTypes;
import io.ballerina.runtime.api.creators.TypeCreator;
import io.ballerina.runtime.api.creators.ValueCreator;
Expand All @@ -27,6 +28,8 @@

import java.util.Map;

import static io.ballerina.stdlib.os.utils.OSConstants.ENV_VAR_KEY;

/**
* Extern function of ballerina.os:listEnv.
*
Expand All @@ -38,13 +41,18 @@ private ListEnv() {

}

public static BMap<BString, Object> listEnv() {
public static BMap<BString, Object> listEnv(Environment env) {
Object envVarMap = env.getStrandLocal(ENV_VAR_KEY);
if (envVarMap != null) {
return (BMap<BString, Object>) envVarMap;
}
MapType mapType = TypeCreator.createMapType(PredefinedTypes.TYPE_STRING);
BMap<BString, Object> envMap = ValueCreator.createMapValue(mapType);
BMap<BString, Object> envMap = ValueCreator.createMapValue(mapType);
Map<String, String> jEnvMap = System.getenv();
for (Map.Entry<String, String> entry : jEnvMap.entrySet()) {
envMap.put(StringUtils.fromString(entry.getKey()), StringUtils.fromString(entry.getValue()));
}
env.setStrandLocal(ENV_VAR_KEY, envMap);
return envMap;
}
}
76 changes: 22 additions & 54 deletions native/src/main/java/io/ballerina/stdlib/os/nativeimpl/SetEnv.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,81 +17,49 @@
*/
package io.ballerina.stdlib.os.nativeimpl;

import io.ballerina.runtime.api.Environment;
import io.ballerina.runtime.api.PredefinedTypes;
import io.ballerina.runtime.api.creators.TypeCreator;
import io.ballerina.runtime.api.creators.ValueCreator;
import io.ballerina.runtime.api.types.MapType;
import io.ballerina.runtime.api.utils.StringUtils;
import io.ballerina.runtime.api.values.BMap;
import io.ballerina.runtime.api.values.BString;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;

import static io.ballerina.stdlib.os.utils.OSConstants.ENV_VAR_KEY;

/**
* Extern function of ballerina.os:setEnv.
*
* @since 1.2.2
*/
public class SetEnv {

private static final String JAVA_LANG_PROCESS_ENVIRONMENT = "java.lang.ProcessEnvironment";
private static final String CASE_INSENSITIVE_ENV = "theCaseInsensitiveEnvironment";

private SetEnv() {

}

@SuppressWarnings("unchecked")
public static Object setEnv(BString key, Object value) {
Map<String, String> env = null;
Map<String, String> writableEnv;
Field field;
if (System.getProperty("os.name").startsWith("Windows")) {
try {
field = Class.forName(JAVA_LANG_PROCESS_ENVIRONMENT).getDeclaredField(CASE_INSENSITIVE_ENV);
} catch (NoSuchFieldException | ClassNotFoundException e) {
return ErrorGenerator.createError("Error while accessing the environment variable map" , e);
public static Object setEnv(Environment env, BString key, Object value) {
Object envVarMap = env.getStrandLocal(ENV_VAR_KEY);
BMap<BString, Object> envMap;
if (envVarMap == null) {
MapType mapType = TypeCreator.createMapType(PredefinedTypes.TYPE_STRING);
envMap = ValueCreator.createMapValue(mapType);
Map<String, String> jEnvMap = System.getenv();
for (Map.Entry<String, String> entry : jEnvMap.entrySet()) {
envMap.put(StringUtils.fromString(entry.getKey()), StringUtils.fromString(entry.getValue()));
}
env.setStrandLocal(ENV_VAR_KEY, envMap);
} else {
env = System.getenv();
try {
field = env.getClass().getDeclaredField("m");
} catch (NoSuchFieldException e) {
return ErrorGenerator.createError("Error while accessing the environment variable map", e);
}
}
try {
disableAccessWarning();
} catch (Exception e) {
return ErrorGenerator.createError("Error disabling illegal access warnings", e);
}
Field finalField = field;
AccessController.doPrivileged((PrivilegedAction) () -> {
finalField.setAccessible(true);
return null;
});
try {
writableEnv = (Map<String, String>) field.get(env);
} catch (IllegalAccessException e) {
return ErrorGenerator.createError("Access denied when trying to modify the environment variable map", e);
envMap = (BMap<BString, Object>) envVarMap;
}
if (value == null) {
writableEnv.remove(key.toString());
envMap.remove(key);
} else {
writableEnv.put(key.toString(), value.toString());
envMap.put(key, value);
}
return null;
}

@SuppressWarnings("unchecked")
private static void disableAccessWarning() throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
AccessController.doPrivileged((PrivilegedAction) () -> {
theUnsafe.setAccessible(true);
return null;
});
Unsafe u = (Unsafe) theUnsafe.get(null);

Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
Field logger = cls.getDeclaredField("logger");
u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public class OSConstants {

static final String PROCESS_FIELD = "ProcessField";

public static final String ENV_VAR_KEY = "io.ballerina.stdlib.os.environment.variables";

private OSConstants() {
}
}

0 comments on commit d4f8c29

Please sign in to comment.