io.microsphere.reflect.AccessibleObjectUtils Maven / Gradle / Ivy
/*
* 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 io.microsphere.reflect;
import io.microsphere.logging.Logger;
import io.microsphere.util.BaseUtils;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Member;
import static io.microsphere.constants.SeparatorConstants.LINE_SEPARATOR;
import static io.microsphere.invoke.MethodHandleUtils.findVirtual;
import static io.microsphere.logging.LoggerFactory.getLogger;
import static io.microsphere.reflect.MemberUtils.asMember;
import static io.microsphere.reflect.MemberUtils.isPublic;
/**
* The utilities class of {@link AccessibleObject}
*
* @author Mercy
* @see AccessibleObject
* @since 1.0.0
*/
public abstract class AccessibleObjectUtils extends BaseUtils {
private static final Logger logger = getLogger(AccessibleObjectUtils.class);
/**
* The method name of {@link AccessibleObject#canAccess(Object)} since JDK 9
*/
private static final String canAccessMethodName = "canAccess";
/**
* The method name of {@link AccessibleObject#trySetAccessible()} since JDK 9
*/
private static final String trySetAccessibleMethodName = "trySetAccessible";
/**
* The {@link MethodHandle} of {@link AccessibleObject#canAccess(Object)} since JDK 9
* if canAccessMethodHandle == null
, it indicates the version of JDK is less than 9
*/
private static final MethodHandle canAccessMethodHandle = findVirtual(AccessibleObject.class, canAccessMethodName, Object.class);
/**
* The {@link MethodHandle} of {@link AccessibleObject#trySetAccessible()} since JDK 9
* if canAccessMethodHandle == null
, it indicates the version of JDK is less than 9
*/
private static final MethodHandle trySetAccessibleMethodHandle = findVirtual(AccessibleObject.class, trySetAccessibleMethodName);
/**
* The class name of {@linkplain java.lang.reflect.InaccessibleObject} since JDK 9
*/
public static final String INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME = "java.lang.reflect.InaccessibleObjectException";
/**
* Try to set the {@link AccessibleObject} accessible.
*
* If JDK >=9 , {@link AccessibleObject#trySetAccessible()} method will be invoked,
* or {@link AccessibleObject#setAccessible(boolean)} method will be invoked if
* {@link AccessibleObject#isAccessible()} is false
.
*
* @param accessibleObject the {@link AccessibleObject} instance
* @return
* @see AccessibleObject#trySetAccessible()
* @see AccessibleObject#setAccessible(boolean)
* @see AccessibleObject#isAccessible()
*/
public static boolean trySetAccessible(AccessibleObject accessibleObject) {
MethodHandle methodHandle = trySetAccessibleMethodHandle;
if (methodHandle == null) { // JDK < 9 or not be initialized
setAccessible(accessibleObject);
return true;
} else { // JDK 9+
return trySetAccessible(methodHandle, accessibleObject);
}
}
/**
* Set the {@link AccessibleObject} accessible.
*
* @param accessibleObject the {@link AccessibleObject} instance
* @return previous accessible status of the {@link AccessibleObject} instance
*/
static boolean setAccessible(AccessibleObject accessibleObject) {
boolean accessible = accessibleObject.isAccessible();
if (!accessible) {
try {
accessibleObject.setAccessible(true);
} catch (RuntimeException e) {
String exceptionClassName = e.getClass().getName();
if (INACCESSIBLE_OBJECT_EXCEPTION_CLASS_NAME.equals(exceptionClassName)) {
// JDK 16+ : JEP 396: Strongly Encapsulate JDK Internals by Default - https://openjdk.org/jeps/396
String errorMessage = "JEP 396: Strongly Encapsulate JDK Internals by Default since JDK 16 - https://openjdk.org/jeps/396 ."
+ LINE_SEPARATOR
+ "It's require to add JVM Options '--add-opens java.base/java.lang.invoke=ALL-UNNAMED' for running";
logger.error(errorMessage, e);
}
throw e;
}
}
return accessible;
}
/**
* Test if the caller can access this reflected object. If this reflected
* object corresponds to an instance method or field then this method tests
* if the caller can access the given {@code obj} with the reflected object.
* For instance methods or fields then the {@code obj} argument must be an
* instance of the {@link Member#getDeclaringClass() declaring class}. For
* static members and constructors then {@code obj} must be {@code null}.
*
* @param object an instance object of the declaring class of this reflected object if it is an instance method or field
* @return {@code true} if the caller can access this reflected object.
*/
public static boolean canAccess(Object object, AccessibleObject accessibleObject) {
Member member = asMember(accessibleObject);
if (isPublic(member)) {
return true;
}
Boolean access = tryCanAccess(object, accessibleObject);
return access == null ? accessibleObject.isAccessible() : access;
}
private static boolean trySetAccessible(MethodHandle methodHandle, AccessibleObject accessibleObject) {
boolean accessible = false;
try {
accessible = (boolean) methodHandle.invokeExact(accessibleObject);
} catch (Throwable e) {
logger.error("java.lang.reflect.AccessibleObject#trySetAccessible() can't be invoked, accessible object : {}",
accessibleObject, e);
}
return accessible;
}
private static Boolean tryCanAccess(Object object, AccessibleObject accessibleObject) {
Boolean access = null;
if (canAccessMethodHandle != null) { // JDK 9+
try {
access = (boolean) canAccessMethodHandle.invokeExact(accessibleObject, object);
} catch (Throwable e) {
logger.error("java.lang.reflect.AccessibleObject#canAccess(Object) can't be invoked, object : {} , accessible object : {}",
object, accessibleObject, e);
}
}
return access;
}
}