All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.seasar.framework.util.AccessibleObjectHandlerJDK9Plus Maven / Gradle / Ivy

The newest version!
/*
 * 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.seasar.framework.util;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import ognl.AccessibleObjectHandler;
import ognl.OgnlRuntime;

// FROM ognl package
/**
 * 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 _clazzUnsafe = instantiateClazzUnsafe();
    private static final Object _unsafeInstance = instantiateUnsafeInstance(_clazzUnsafe);
    private static final Method _unsafeObjectFieldOffsetMethod = instantiateUnsafeObjectFieldOffsetMethod(_clazzUnsafe);
    private static final Method _unsafePutBooleanMethod = instantiateUnsafePutBooleanMethod(_clazzUnsafe);
    private static final Field _accessibleObjectOverrideField = instantiateAccessibleObjectOverrideField();
    private static final long _accessibleObjectOverrideFieldOffset = 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 (_clazzUnsafe != null ? _clazzUnsafe.isAssignableFrom(clazz) : false);
    }

    /**
     * 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.
     *
     * @param clazz (expected to be an Unsafe instance)
     * @return instance if available, null otherwise
     */
    private static Object instantiateUnsafeInstance(Class clazz) {
        Object unsafe;

        if (clazz != null) {
            Field field = null;
            try {
                field = clazz.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.
     *
     * @param clazz (expected to be an Unsafe instance)
     * @return method if available, null otherwise
     */
    private static Method instantiateUnsafeObjectFieldOffsetMethod(Class clazz) {
        Method method;

        if (clazz != null) {
            try {
                method = clazz.getMethod("objectFieldOffset", Field.class);
            } catch (Throwable t) {
                method = null;
            }
        } else {
            method = null;
        }

        return method;
    }

    /**
     * Instantiate an Unsafe.putBoolean() method instance.
     *
     * @param clazz (expected to be an Unsafe instance)
     * @return method if available, null otherwise
     */
    private static Method instantiateUnsafePutBooleanMethod(Class clazz) {
        Method method;

        if (clazz != null) {
            try {
                method = clazz.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 determined the AccessibleObject override field offset.
     *
     * @return field offset if available, -1 otherwise
     */
    private static long determineAccessibleObjectOverrideFieldOffset() {
        long offset = -1;

        if (_accessibleObjectOverrideField != null && _unsafeObjectFieldOffsetMethod != null && _unsafeInstance != null) {
            try {
                offset = (Long) _unsafeObjectFieldOffsetMethod.invoke(_unsafeInstance, _accessibleObjectOverrideField);
            } 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.
     */
    @Override
    public void setAccessible(AccessibleObject accessibleObject, boolean flag) {
        boolean operationComplete = false;

        if (_unsafeInstance != null && _unsafePutBooleanMethod != null && _accessibleObjectOverrideFieldOffset != -1) {
            try {
                _unsafePutBooleanMethod.invoke(_unsafeInstance, accessibleObject, _accessibleObjectOverrideFieldOffset, 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);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy