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

com.feilong.lib.ognl.AccessibleObjectHandlerJDK9Plus Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show 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 com.feilong.lib.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  _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();
        }
        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 - 2024 Weber Informatics LLC | Privacy Policy