Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically exposes jdk modules #1340

Merged
merged 5 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sofa-boot-project/sofa-boot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 com.alipay.sofa.boot.Initializer;

import com.alipay.sofa.boot.util.ModuleUtil;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

/**
* @author huazhongming
* @since 4.4.0
*/
public class AutoModuleExportApplicationContextInitializer
implements
ApplicationContextInitializer<ConfigurableApplicationContext> {

private static final String AUTO_MODULE_JDK_ENABLE_KEY = "sofa.boot.auto.module.export.jdk.enable";
private static final String AUTO_MODULE_ALL_ENABLE_KEY = "sofa.boot.auto.module.export.all.enable";

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
if (isEnable(applicationContext, AUTO_MODULE_ALL_ENABLE_KEY, "false")) {
ModuleUtil.exportAllModulePackageToAll();
} else if (isEnable(applicationContext, AUTO_MODULE_JDK_ENABLE_KEY, "true")) {
ModuleUtil.exportAllJDKModulePackageToAll();
}
}

protected boolean isEnable(ConfigurableApplicationContext applicationContext, String key,
String defaultValue) {
String switchStr = applicationContext.getEnvironment().getProperty(key, defaultValue);
return Boolean.parseBoolean(switchStr);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 com.alipay.sofa.boot.util;

import com.alipay.sofa.boot.log.SofaBootLoggerFactory;
import org.slf4j.Logger;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* @author huazhongming
* @since 4.4.0
*/
public class ModuleUtil {

Check warning on line 32 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L32

Added line #L32 was not covered by tests

private static final Logger LOGGER = SofaBootLoggerFactory
.getLogger(ModuleUtil.class);

private static final MethodHandle implAddOpensToAllUnnamed;

private static final MethodHandle implAddOpens;

private static final MethodHandle implAddExportsToAllUnnamed;

private static final MethodHandle implAddExports;

private static final Map<String, Module> nameToModules;

private static final AtomicBoolean isExported = new AtomicBoolean(false);

static {
implAddOpensToAllUnnamed = createModuleMethodHandle("implAddOpensToAllUnnamed",
String.class);
implAddOpens = createModuleMethodHandle("implAddOpens", String.class);
implAddExportsToAllUnnamed = createModuleMethodHandle("implAddExportsToAllUnnamed",
String.class);
implAddExports = createModuleMethodHandle("implAddExports", String.class);
nameToModules = getNameToModule();
}

/**
* Export all JDK module packages to all.
*/
public static void exportAllJDKModulePackageToAll() {
try {
if (isExported.compareAndSet(false,true) && nameToModules != null) {
nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> {
if (isJDKModulePackage(pkgName)) {
addOpensToAll(module, pkgName);
addExportsToAll(module, pkgName);
}
}));
}
} catch (Throwable t) {
LOGGER.error("Failed to export all JDK module package to all", t);

Check warning on line 73 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L72-L73

Added lines #L72 - L73 were not covered by tests
}
}

private static boolean isJDKModulePackage(String modulePackageName) {
return modulePackageName.startsWith("java.") || modulePackageName.startsWith("jdk.");
}

/**
* Export all module packages to all.
*/
public static void exportAllModulePackageToAll() {
try {
if (isExported.compareAndSet(false,true) && nameToModules != null) {
nameToModules.forEach((name, module) -> module.getPackages().forEach(pkgName -> {
addOpensToAll(module, pkgName);
addExportsToAll(module, pkgName);
}));

Check warning on line 90 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L87-L90

Added lines #L87 - L90 were not covered by tests
}
} catch (Throwable t) {
LOGGER.error("Failed to export all module package to all", t);
}
}

Check warning on line 95 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L92-L95

Added lines #L92 - L95 were not covered by tests

/**
* Updates this module to open a package to all unnamed modules.
*
* @param moduleName
* @param packageName
*/
public static boolean addOpensToAllUnnamed(String moduleName, String packageName) {
return invokeModuleMethod(implAddOpensToAllUnnamed, moduleName, packageName);

Check warning on line 104 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L104

Added line #L104 was not covered by tests
}

/**
* Updates this module to open a package to all unnamed modules.
*
* @param module
* @param packageName
*/
public static boolean addOpensToAllUnnamed(Module module, String packageName) {
return invokeModuleMethod(implAddOpensToAllUnnamed, module, packageName);

Check warning on line 114 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L114

Added line #L114 was not covered by tests
}

/**
* Updates this module to export a package to all unnamed modules.
*
* @param moduleName
* @param packageName
*/
public static boolean addExportsToAllUnnamed(String moduleName, String packageName) {
return invokeModuleMethod(implAddExportsToAllUnnamed, moduleName, packageName);

Check warning on line 124 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L124

Added line #L124 was not covered by tests
}

/**
* Updates this module to export a package to all unnamed modules.
*
* @param module
* @param packageName
*/
public static boolean addExportsToAllUnnamed(Module module, String packageName) {
return invokeModuleMethod(implAddExportsToAllUnnamed, module, packageName);

Check warning on line 134 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L134

Added line #L134 was not covered by tests
}

/**
* Updates this module to open a package to another module.
*
* @param moduleName
* @param packageName
*/
public static boolean addOpensToAll(String moduleName, String packageName) {

return invokeModuleMethod(implAddOpens, moduleName, packageName);

Check warning on line 145 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L145

Added line #L145 was not covered by tests
}

/**
* Updates this module to open a package to another module.
*
* @param module
* @param packageName
*/
public static boolean addOpensToAll(Module module, String packageName) {

return invokeModuleMethod(implAddOpens, module, packageName);
}

/**
* Updates this module to export a package unconditionally.
* @param moduleName
* @param packageName
*/
public static boolean addExportsToAll(String moduleName, String packageName) {
return invokeModuleMethod(implAddExports, moduleName, packageName);

Check warning on line 165 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L165

Added line #L165 was not covered by tests
}

/**
* Updates this module to export a package unconditionally.
* @param module
* @param packageName
*/
public static boolean addExportsToAll(Module module, String packageName) {
return invokeModuleMethod(implAddExports, module, packageName);
}

/**
* invoke ModuleLayer method
*
* @param method
* @param moduleName
* @param packageName
* @return
*/
public static boolean invokeModuleMethod(MethodHandle method, String moduleName,
String packageName) {
Comment on lines +185 to +186
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add null checks for method handles to prevent NullPointerException

The method parameter may be null if the method handle creation fails. Invoking method.invoke(...) without checking for null can lead to a NullPointerException. Please add a null check before using the method handle.

Apply this diff to add null checks:

 public static boolean invokeModuleMethod(MethodHandle method, String moduleName,
                                          String packageName) {
+    if (method == null) {
+        LOGGER.error("Method handle is null for module: {}, package: {}", moduleName, packageName);
+        return false;
+    }
     Optional<Module> findModule = ModuleLayer.boot().findModule(moduleName);
     if (findModule.isPresent()) {
         try {
             return invokeModuleMethod(method, findModule.get(), packageName);
         } catch (Throwable t) {
             LOGGER.error("Failed to invoke ModuleLayer method: {}", method, t);
         }
     }
     return false;
 }

 public static boolean invokeModuleMethod(MethodHandle method, Module module, String packageName) {
+    if (method == null) {
+        LOGGER.error("Method handle is null for module: {}, package: {}", module.getName(), packageName);
+        return false;
+    }
     try {
         method.invoke(module, packageName);
         return true;
     } catch (Throwable t) {
         LOGGER.error("Failed to invoke Module method: {}", method, t);
     }
     return false;
 }

Also applies to: 201-202

Optional<Module> findModule = ModuleLayer.boot().findModule(moduleName);

Check warning on line 187 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L187

Added line #L187 was not covered by tests
if (findModule.isPresent()) {
try {
return invokeModuleMethod(method, findModule.get(), packageName);
} catch (Throwable t) {
LOGGER.error("Failed to invoke ModuleLayer method: {}", method, t);

Check warning on line 192 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L190-L192

Added lines #L190 - L192 were not covered by tests
}
}
return false;

Check warning on line 195 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L195

Added line #L195 was not covered by tests
}

public static boolean invokeModuleMethod(MethodHandle method, Module module, String packageName) {
try {
method.invoke(module, packageName);
return true;
} catch (Throwable t) {
LOGGER.error("Failed to invoke Module method: {}", method, t);

Check warning on line 203 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L202-L203

Added lines #L202 - L203 were not covered by tests
}
return false;

Check warning on line 205 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L205

Added line #L205 was not covered by tests
}

/**
* create MethodHandle from Module
*
* @param methodName
* @param parameterTypes
* @return MethodHandle
*/
private static MethodHandle createModuleMethodHandle(String methodName,
Class<?>... parameterTypes) {
try {
return UnsafeUtil.implLookup().unreflect(
Module.class.getDeclaredMethod(methodName, parameterTypes));
} catch (Throwable t) {
LOGGER.error("Failed to create Module method handle: {}", methodName, t);

Check warning on line 221 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L220-L221

Added lines #L220 - L221 were not covered by tests
}
return null;

Check warning on line 223 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L223

Added line #L223 was not covered by tests
}

/**
* Get ModuleLayer.bootLayer field value
*
* @param fieldName
* @return field value
*/
private static Object getModuleLayerFieldsValue(String fieldName) {
ModuleLayer moduleLayer = ModuleLayer.boot();
try {
Class<ModuleLayer> moduleLayerClass = ModuleLayer.class;
Field field = moduleLayerClass.getDeclaredField(fieldName);
return UnsafeUtil.implLookup().unreflectVarHandle(field).get(moduleLayer);
} catch (Throwable t) {
LOGGER.error("Failed to get ModuleLayer field value: {}", fieldName, t);

Check warning on line 239 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L238-L239

Added lines #L238 - L239 were not covered by tests
}
return null;

Check warning on line 241 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/ModuleUtil.java#L241

Added line #L241 was not covered by tests
}

/**
* Get all modules from System.bootLayer
*
* @return modules
*/
@SuppressWarnings("unchecked")
public static Map<String, Module> getNameToModule() {
return (Map<String, Module>) getModuleLayerFieldsValue("nameToModule");
}
Comment on lines +251 to +252
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle potential ClassCastException in getNameToModule()

Casting the result of getModuleLayerFieldsValue("nameToModule") to (Map<String, Module>) may result in a ClassCastException if the field does not contain the expected type. Consider adding a type check before casting to ensure safety.

Apply this diff to safely handle the cast:

 public static Map<String, Module> getNameToModule() {
-    return (Map<String, Module>) getModuleLayerFieldsValue("nameToModule");
+    Object moduleMap = getModuleLayerFieldsValue("nameToModule");
+    if (moduleMap instanceof Map) {
+        return (Map<String, Module>) moduleMap;
+    } else {
+        LOGGER.error("Expected nameToModule to be a Map<String, Module>, but got: {}", moduleMap);
+        return null;
+    }
 }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (Map<String, Module>) getModuleLayerFieldsValue("nameToModule");
}
public static Map<String, Module> getNameToModule() {
Object moduleMap = getModuleLayerFieldsValue("nameToModule");
if (moduleMap instanceof Map) {
return (Map<String, Module>) moduleMap;
} else {
LOGGER.error("Expected nameToModule to be a Map<String, Module>, but got: {}", moduleMap);
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 com.alipay.sofa.boot.util;

import sun.misc.Unsafe;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;

/**
* @author huazhongming
* @since 4.4.0
*/
public class UnsafeUtil {

Check warning on line 28 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java#L28

Added line #L28 was not covered by tests
private static Unsafe UNSAFE;
private static MethodHandles.Lookup IMPL_LOOKUP;

public static Unsafe unsafe() {
if (UNSAFE == null) {
Unsafe unsafe = null;
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
unsafe = (Unsafe) theUnsafeField.get(null);
} catch (Throwable ignored) {

Check warning on line 39 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java#L39

Added line #L39 was not covered by tests
}
UNSAFE = unsafe;
}

return UNSAFE;
}

public static MethodHandles.Lookup implLookup() {
if (IMPL_LOOKUP == null) {
Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;

try {
Field implLookupField = lookupClass.getDeclaredField("IMPL_LOOKUP");
long offset = unsafe().staticFieldOffset(implLookupField);
IMPL_LOOKUP = (MethodHandles.Lookup) unsafe().getObject(
unsafe().staticFieldBase(implLookupField), offset);
} catch (Throwable ignored) {

Check warning on line 56 in sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java

View check run for this annotation

Codecov / codecov/patch

sofa-boot-project/sofa-boot/src/main/java/com/alipay/sofa/boot/util/UnsafeUtil.java#L56

Added line #L56 was not covered by tests
}
}
return IMPL_LOOKUP;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ org.springframework.boot.SpringApplicationRunListener=\

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer
com.alipay.sofa.boot.compatibility.CompatibilityVerifierApplicationContextInitializer,\
com.alipay.sofa.boot.Initializer.AutoModuleExportApplicationContextInitializer
Loading
Loading