org.apache.ibatis.ognl.AccessibleObjectHandlerJDK9Plus Maven / Gradle / Ivy
Show all versions of org.apache.servicemix.bundles.mybatis
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* and/or LICENSE 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 org.apache.ibatis.ognl;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Utilizes a JDK 9 and later mechanism for changing the accessibility level of a given
* AccessibleObject.
*
* If the JDK 9+ mechanism fails, this class will fall back to a standard pre-JDK 9 reflection mechanism.
* Note: That may cause "WARNING: Illegal reflective access" output to be generated to stdout/stderr.
*
* For reference, this class draws on information from the following locations:
* - Post about Illegal Reflective Access what is an illegal reflective access
* - Blog on Unsafe Java Magic. Part 4: sun.misc.Unsafe
* - Blog on Unsafe Guide to sun.misc.Unsafe
* - JEP about access to Unsafe being retained in JDK 9 JEP 260: Encapsulate Most Internal APIs
*
* In addition to the above, inspiration was drawn from Gson: PR 1218,
* PR 1306.
*
* Appreciation and credit to the authors, contributors and commenters for the information contained in the preceding links.
*
* @since 3.1.24
*/
class AccessibleObjectHandlerJDK9Plus implements AccessibleObjectHandler {
private static final Class> CLAZZ_UNSAFE = instantiateClazzUnsafe();
private static final Object UNSAFE_INSTANCE = instantiateUnsafeInstance();
private static final Method UNSAFE_OBJECT_FIELD_OFFSET_METHOD = instantiateUnsafeObjectFieldOffsetMethod();
private static final Method UNSAFE_PUT_BOOLEAN_METHOD = instantiateUnsafePutBooleanMethod();
private static final Field ACCESSIBLE_OBJECT_OVERRIDE_FIELD = instantiateAccessibleObjectOverrideField();
private static final long ACCESSIBLE_OBJECT_OVERRIDE_FIELD_OFFSET = determineAccessibleObjectOverrideFieldOffset();
/**
* Private constructor
*/
private AccessibleObjectHandlerJDK9Plus() {
}
/**
* Package-accessible method to determine if a given class is Unsafe or a descendant
* of Unsafe.
*
* @param clazz the Class upon which to perform the unsafe check.
* @return true if parameter is Unsafe or a descendant, false otherwise
*/
static boolean unsafeOrDescendant(final Class> clazz) {
return (CLAZZ_UNSAFE != null && CLAZZ_UNSAFE.isAssignableFrom(clazz));
}
/**
* Instantiate an instance of the Unsafe class.
*
* @return class if available, null otherwise
*/
private static Class> instantiateClazzUnsafe() {
Class> clazz;
try {
clazz = Class.forName("sun.misc.Unsafe");
} catch (Throwable t) {
clazz = null;
}
return clazz;
}
/**
* Instantiate an instance of Unsafe object.
*
* @return instance if available, null otherwise
*/
private static Object instantiateUnsafeInstance() {
Object unsafe;
if (AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE != null) {
Field field = null;
try {
field = AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = field.get(null);
} catch (Throwable t) {
unsafe = null;
} finally {
if (field != null) {
try {
field.setAccessible(false);
} catch (Throwable t) {
// Don't care
}
}
}
} else {
unsafe = null;
}
return unsafe;
}
/**
* Instantiate an Unsafe.objectFieldOffset() method instance.
*
* @return method if available, null otherwise
*/
private static Method instantiateUnsafeObjectFieldOffsetMethod() {
Method method;
if (AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE != null) {
try {
method = AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE.getMethod("objectFieldOffset", Field.class);
} catch (Throwable t) {
method = null;
}
} else {
method = null;
}
return method;
}
/**
* Instantiate an Unsafe.putBoolean() method instance.
*
* @return method if available, null otherwise
*/
private static Method instantiateUnsafePutBooleanMethod() {
Method method;
if (AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE != null) {
try {
method = AccessibleObjectHandlerJDK9Plus.CLAZZ_UNSAFE.getMethod("putBoolean", Object.class, long.class, boolean.class);
} catch (Throwable t) {
method = null;
}
} else {
method = null;
}
return method;
}
/**
* Instantiate an AccessibleObject override field instance.
*
* @return field if available, null otherwise
*/
private static Field instantiateAccessibleObjectOverrideField() {
Field field;
try {
field = AccessibleObject.class.getDeclaredField("override");
} catch (Throwable t) {
field = null;
}
return field;
}
/**
* Attempt to determine the AccessibleObject override field offset.
*
* @return field offset if available, -1 otherwise
*/
private static long determineAccessibleObjectOverrideFieldOffset() {
long offset = -1;
if (ACCESSIBLE_OBJECT_OVERRIDE_FIELD != null && UNSAFE_OBJECT_FIELD_OFFSET_METHOD != null && UNSAFE_INSTANCE != null) {
try {
offset = (Long) UNSAFE_OBJECT_FIELD_OFFSET_METHOD.invoke(UNSAFE_INSTANCE, ACCESSIBLE_OBJECT_OVERRIDE_FIELD);
} catch (Throwable t) {
// Don't care (offset already -1)
}
}
return offset;
}
/**
* Package-level generator of an AccessibleObjectHandlerJDK9Plus instance.
*
* Not intended for use outside of the package.
*
* Note: An AccessibleObjectHandlerJDK9Plus will only be created if running on a
* JDK9+ and the environment flag is set. Otherwise this method will return
* an AccessibleHandlerPreJDK9 instance instead,
*
* @return an AccessibleObjectHandler instance
* @since 3.1.24
*/
static AccessibleObjectHandler createHandler() {
if (OgnlRuntime.usingJDK9PlusAccessHandler()) {
return new AccessibleObjectHandlerJDK9Plus();
} else {
return AccessibleObjectHandlerPreJDK9.createHandler();
}
}
/**
* Utilize accessibility modification mechanism for JDK 9 (Java Major Version 9) and later.
* Should that mechanism fail, attempt a standard pre-JDK9 accessibility modification.
*
* @param accessibleObject the AccessibleObject upon which to apply the flag.
* @param flag the new accessible flag value.
*/
public void setAccessible(AccessibleObject accessibleObject, boolean flag) {
boolean operationComplete = false;
if (UNSAFE_INSTANCE != null && UNSAFE_PUT_BOOLEAN_METHOD != null && ACCESSIBLE_OBJECT_OVERRIDE_FIELD_OFFSET != -1) {
try {
UNSAFE_PUT_BOOLEAN_METHOD.invoke(UNSAFE_INSTANCE, accessibleObject, ACCESSIBLE_OBJECT_OVERRIDE_FIELD_OFFSET, flag);
operationComplete = true;
} catch (Throwable t) {
// Don't care (operationComplete already false)
}
}
if (!operationComplete) {
// Fallback to standard reflection if Unsafe processing fails
accessibleObject.setAccessible(flag);
}
}
}