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

com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil Maven / Gradle / Ivy

The newest version!
/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */

package com.amazonaws.services.lambda.runtime.serialization.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.Type;

import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R0;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R1;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R2;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R3;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R4;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R5;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.R9;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.V1;
import com.amazonaws.services.lambda.runtime.serialization.util.Functions.V2;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;

/**
 * Class with reflection utilities
 */
public final class ReflectUtil {

    private ReflectUtil() {
    }

    /**
     * Copy a class from one class loader to another. Was previously used in AwsJackson class to do some crazy
     * passing of classes from class loader to class loader. When that class was removed due to excessive complexity,
     * this method was retained for potential future use.
     * @param clazz class to copy
     * @param cl class loader to copy class to
     * @return Class inside new classloader
     * @throws UncheckedIOException if class cannot be copied
     * @throws ReflectException if class cannot be read after copying
     */
    @SuppressWarnings({"unchecked"})
    public static Class copyClass(Class clazz, ClassLoader cl) {
        // if class exists in target class loader then just load that class and return
        try {
            return cl.loadClass(clazz.getName());
        } catch (ClassNotFoundException e) {}
        // copy class to target class loader
        LambdaByteArrayOutputStream stream;
        // 1 kb
        final int chunkSize = 1024;
        final String resourceName = clazz.getName().replace('.', '/') + ".class";
        try(InputStream input = clazz.getClassLoader().getResourceAsStream(resourceName)) {
            int initial = Math.max(chunkSize, input.available());
            stream = new LambdaByteArrayOutputStream(initial);
            stream.readAll(input);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
        // load class from target class loader
        try {
            Functions.R5, ClassLoader, String, byte[], Integer, Integer> defineClassMethod =
                    ReflectUtil.loadInstanceR4(ClassLoader.class, "defineClass", true,
                            (Class>)(Class)Class.class, String.class, byte[].class, int.class, int.class);
            Class result = defineClassMethod.call(cl, clazz.getName(), stream.getRawBuf(), 0, stream.getValidByteCount());
            V2> resolveClass =
                    ReflectUtil.loadInstanceV1(ClassLoader.class, "resolveClass", true, Class.class);
            resolveClass.call(cl, result);
            return result;
        } catch (ClassFormatError | SecurityException e) {
            throw new ReflectException(e);
        }
    }

    public static Class loadClass(ClassLoader cl, String name) {
        try {
            return Class.forName(name, true, cl);
        } catch(ClassNotFoundException | LinkageError e) {
            throw new ReflectException(e);
        }
    }

    public static class ReflectException extends RuntimeException {

        private static final long serialVersionUID = 1L;

        public ReflectException() {
            super();
            // TODO Auto-generated constructor stub
        }

        public ReflectException(String message, Throwable cause,
                boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }

        public ReflectException(String message, Throwable cause) {
            super(message, cause);
        }

        public ReflectException(String message) {
            super(message);
        }

        public ReflectException(Throwable cause) {
            super(cause);
        }

    }

    private static  T newInstance(Constructor constructor, Object... params) {
        try {
            return constructor.newInstance(params);
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new ReflectException(e);
        }
    }

    
    public static Class getRawClass(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getRawClass(((ParameterizedType)type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Class componentRaw = getRawClass(((GenericArrayType)type).getGenericComponentType());
            return Array.newInstance(componentRaw, 0).getClass();
        } else if (type instanceof TypeVariable) {
            throw new ReflectException("type variables not supported");
        } else {
            throw new ReflectException("unsupport type: " + type.getClass().getName());
        }
    }

    public static R1 makeCaster(Type type) {
        return makeCaster(getRawClass(type));
    }

    private static  R1 boxCaster(final Class clazz) {
        return new R1() {
            public T call(Object o) {
                return clazz.cast(o);
            }
        };
    }

    @SuppressWarnings("unchecked")
    public static  R1 makeCaster(Class clazz) {
        if(long.class.equals(clazz)) {
            return (R1)boxCaster(Long.class);
        } else if (double.class.equals(clazz)) {
            return (R1)boxCaster(Double.class);
        } else if (float.class.equals(clazz)) {
            return (R1)boxCaster(Float.class);
        } else if (int.class.equals(clazz)) {
            return (R1)boxCaster(Integer.class);
        } else if (short.class.equals(clazz)) {
            return (R1)boxCaster(Short.class);
        } else if (char.class.equals(clazz)) {
            return (R1)boxCaster(Character.class);
        } else if (byte.class.equals(clazz)) {
            return (R1)boxCaster(Byte.class);
        } else if (boolean.class.equals(clazz)) {
            return (R1)boxCaster(Boolean.class);
        } else {
            return boxCaster(clazz);
        }
    }

    private static  T invoke(Method method, Object instance, Class rType, Object... params) {
        final R1 caster = makeCaster(rType);
        try {
            Object result = method.invoke(instance, params);
            if(rType.equals(Void.TYPE)) {
                return null;
            } else {
                return caster.call(result);
            }
        } catch(InvocationTargetException | ExceptionInInitializerError | IllegalAccessException e) {
            throw new ReflectException(e);
        }
    }

    private static Method lookupMethod(Class clazz, String name, Class... pTypes) {
        try {
            try {
                return clazz.getDeclaredMethod(name, pTypes);
            } catch (NoSuchMethodException e) {
                return clazz.getMethod(name, pTypes);
            }
        } catch (NoSuchMethodException | SecurityException e) {
            throw new ReflectException(e);
        }
    }

    private static Method getDeclaredMethod(Class clazz, String name, boolean isStatic, 
            boolean setAccessible, Class rType, 
            Class... pTypes) {
        final Method method = lookupMethod(clazz, name, pTypes);

        if (!rType.equals(Void.TYPE) && !rType.isAssignableFrom(method.getReturnType())) {
            throw new ReflectException("Class=" + clazz.getName() + " method="
                    + name + " type " + method.getReturnType().getName() + " not assignment-compatible with "
                    + rType.getName());
        }

        int mods = method.getModifiers();
        if (Modifier.isStatic(mods) != isStatic) {
            throw new ReflectException("Class=" + clazz.getName() + " method="
                    + name + " expected isStatic=" + isStatic);
        }

        if (setAccessible) {
            method.setAccessible(true);
        }
        return method;
    }

    private static  Constructor getDeclaredConstructor(Class clazz, boolean setAccessible, Class... pTypes) {
        final Constructor constructor;
        try {
            constructor = clazz.getDeclaredConstructor(pTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            throw new ReflectException(e);
        }

        if (setAccessible) {
            constructor.setAccessible(true);
        }
        return constructor;
    }

    /**
     * load instance method that takes no parameters and returns type R
     * @param clazz Class of instance
     * @param name name of method
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param  instance class type
     * @param  return type
     * @return function handle
     */
    public static  R1 loadInstanceR0(Class clazz, String name,
                                                 boolean setAccessible, final Class rType) {
        final Method method = getDeclaredMethod(clazz, name, false, setAccessible, rType);
        return new R1() {
            public R call(C instance) {
                return invoke(method, instance, rType);
            }
        };
    }

    /**
     * load instance method that takes 4 parameters and return type R
     * @param clazz class of instance
     * @param name name of method
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param a3Type argument 3 class
     * @param a4Type argument 4 class
     * @param  argument 1 type
     * @param  argument 2 type
     * @param  argument 3 type
     * @param  argument 4 type
     * @param  instance class type
     * @param  return type
     * @return function handle
     */
    public static  R5 loadInstanceR4(Class clazz,
                                                                                 String name,
                                                                                 boolean setAccessible,
                                                                                 final Class rType,
                                                                                 Class a1Type,
                                                                                 Class a2Type,
                                                                                 Class a3Type,
                                                                                 Class a4Type) {
        final Method method = getDeclaredMethod(clazz, name, false, setAccessible, rType, a1Type, a2Type, a3Type, a4Type);
        return new R5() {
            public R call(C instance, A1 a1, A2 a2, A3 a3, A4 a4) {
                return invoke(method, instance, rType, a1, a2, a3, a4);
            }
        };
    }

    /**
     * load an instance method that take 1 parameter and does not return anything
     * @param clazz class of instance
     * @param name name of method
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param  instance class type
     * @param  argument 1 type
     * @return function handle
     */
    public static  V2 loadInstanceV1(final Class clazz, String name, boolean setAccessible,
                                                   final Class a1Type) {
        final Method method = getDeclaredMethod(clazz, name, false, setAccessible, Void.TYPE, a1Type);
        return new V2() {
            public void call(C instance, A1 a1) {
                invoke(method, instance, Void.TYPE, a1);
            }
        };
    }

    /**
     * load an instance method that takes no parameters and return type R
     * @param instance instance to load method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param  instance type
     * @param  return type
     * @return function handle
     */
    public static  R0 bindInstanceR0(final C instance, String name, boolean setAccessible,
            final Class rType) {
        final Method method = getDeclaredMethod(instance.getClass(), name, false, setAccessible, rType);
        return new R0() {
            public R call() {
                return invoke(method, instance, rType);
            }
        };
    }

    /**
     * load an instance method that takes 1 parameter and returns type R
     * @param instance instance to load method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param a1Type class of argument 1
     * @param  instance type
     * @param  argument 1 type
     * @param  return type
     * @return function handle
     */
    public static  R1 bindInstanceR1(final C instance, String name, boolean setAccessible,
                                                      final Class rType, Class a1Type) {
        final Method method = getDeclaredMethod(instance.getClass(), name, false, setAccessible, rType, a1Type);
        return new R1() {
            public R call(A1 a1) {
                return invoke(method, instance, rType, a1);
            }
        };
    }

    /**
     * * load an instance method that takes 1 parameter and returns nothing
     * @param instance instance to load method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type class of argument 1
     * @param  instance type
     * @param  argument 1 type
     * @return function handle
     */
    public static  V1 bindInstanceV1(final C instance, String name, boolean setAccessible,
    final Class a1Type) {
        final Method method = getDeclaredMethod(instance.getClass(), name, false, setAccessible, Void.TYPE, a1Type);
        return new V1() {
            public void call(A1 a1) {
                invoke(method, instance, Void.TYPE, a1);
            }
        };
    }

    /**
     * load an instance method that takes 2 parameter and returns nothing
     * @param instance instance to load method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type class of argument 1
     * @param a2Type class of argument 2
     * @param  instance type
     * @param  argument 1 type
     * @param  argument 2 type
     * @return function handle
     */
    public static  V2 bindInstanceV2(final C instance, String name, boolean setAccessible,
    final Class a1Type, final Class a2Type) {
        final Method method = getDeclaredMethod(instance.getClass(), name, false, setAccessible, Void.TYPE, a1Type, a2Type);
        return new V2() {
            public void call(A1 a1, A2 a2) {
                invoke(method, instance, Void.TYPE, a1, a2);
            }
        };
    }

    /**
     * load static method that takes no parameters and returns type R
     * @param clazz class to load static method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param  return type
     * @return function handle
     */
    public static  R0 loadStaticR0(Class clazz, String name, boolean setAccessible,
    final Class rType) {
        final Method method = getDeclaredMethod(clazz, name, true, setAccessible, rType);
        return new R0() {
            public R call() {
                return invoke(method, null, rType);
            }
        };
    }

    /**
     * load static method that takes one parameter and returns type R
     * @param clazz class to load static method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param rType class of return type
     * @param a1Type argument 1 class
     * @param  return type
     * @param  argument 1 type
     * @return function handle
     */
    public static  R1 loadStaticR1(Class clazz, String name, boolean setAccessible,
    final Class rType, Class a1Type) {
        final Method method = getDeclaredMethod(clazz, name, true, setAccessible, rType, a1Type);
        return new R1() {
            public R call(A1 a1) {
                return invoke(method, null, rType, a1);
            }
        };
    }

    /**
     * load static method that takes two parameters and return nothing
     * @param clazz class to load static method from
     * @param name method name
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param  argument 1 type
     * @param  argument 2 type
     * @return function handle
     */
    public static  V2 loadStaticV2(Class clazz, String name, boolean setAccessible,
                                                   final Class a1Type, final Class a2Type) {
        final Method method = getDeclaredMethod(clazz, name, true, setAccessible, Void.TYPE, a1Type, a2Type);
        return new V2() {
            public void call(A1 a1, A2 a2) {
                invoke(method, null, Void.TYPE, a1, a2);
            }
        };
    }

    /**
     * load default constructor
     * @param clazz Class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param  Class type
     * @return function handle
     */
    public static  R0 loadConstructor0(final Class clazz, boolean setAccessible) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible);
        return new R0() {
            public C call() {
                return newInstance(constructor);
            }
        };
    }

    /**
     * load constructor that takes 1 parameter
     * @param clazz Class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param  Class type
     * @param  argument 1 type
     * @return function handle
     */
    public static  R1 loadConstructor1(final Class clazz, boolean setAccessible,
                                                     Class a1Type) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible, a1Type);
        return new R1() {
            public C call(A1 a1) {
                return newInstance(constructor, a1);
            }
        };
    }

    /**
     * load constructor that takes 2 parameters
     * @param clazz Class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param  Class type
     * @param  argument 1 type
     * @param  argument 2 type
     * @return function handle
     */
    public static  R2 loadConstructor2(final Class clazz, boolean setAccessible,
                                                             Class a1Type, Class a2Type) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible, a1Type, a2Type);
        return new R2() {
            public C call(A1 a1, A2 a2) {
                return newInstance(constructor, a1, a2);
            }
        };
    }

    /**
     * load constuctor that takes 3 parameters
     * @param clazz class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param a3Type argument 3 class
     * @param  Class type
     * @param  argument 1 type
     * @param  argument 2 type
     * @param  argument 3 type
     * @return function handle
     */
    public static  R3 loadConstuctor3(final Class clazz, boolean setAccessible,
                                                                    Class a1Type, Class a2Type,
                                                                    Class a3Type) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible, a1Type, a2Type, a3Type);
        return new R3() {
            public C call(A1 a1, A2 a2, A3 a3) {
                return newInstance(constructor, a1, a2, a3);
            }
        };
    }

    /**
     * loads constructor that takes 4 parameters
     * @param clazz class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param a3Type argument 3 class
     * @param a4Type argument 4 class
     * @param  Class type
     * @param  argument 1 type
     * @param  argument 2 type
     * @param  argument 3 type
     * @param  argument 4 type
     * @return function handle
     */
    public static  R4 loadConstuctor4(final Class clazz,
                                                                            boolean setAccessible,
                                                                            Class a1Type,
                                                                            Class a2Type,
                                                                            Class a3Type,
                                                                            Class a4Type) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible, a1Type, a2Type, a3Type, a4Type);
        return new R4() {
            public C call(A1 a1, A2 a2, A3 a3, A4 a4) {
                return newInstance(constructor, a1, a2, a3, a4);
            }
        };
    }

    /**
     * loads constructor that takes 5 paramters
     * @param clazz class to load constructor for
     * @param setAccessible whether method is accessible (public vs private)
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param a3Type argument 3 class
     * @param a4Type argument 4 class
     * @param a5Type argument 5 class
     * @param  Class type
     * @param  argument 1 type
     * @param  argument 2 type
     * @param  argument 3 type
     * @param  argument 4 type
     * @param  argument 5 type
     * @return function handle
     */
    public static  R5 loadConstuctor5(final Class clazz,
                                                                                    boolean setAccessible,
                                                                                    Class a1Type,
                                                                                    Class a2Type,
                                                                                    Class a3Type,
                                                                                    Class a4Type,
                                                                                    Class a5Type) {
        final Constructor constructor = getDeclaredConstructor(clazz, setAccessible, a1Type, a2Type, a3Type, a4Type, a5Type);
        return new R5() {
            public C call(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
                return newInstance(constructor, a1, a2, a3, a4, a5);
            }
        };
    }

    /**
     * load constuctor that takes 9 parameters
     * @param a1Type argument 1 class
     * @param a2Type argument 2 class
     * @param a3Type argument 3 class
     * @param a4Type argument 4 class
     * @param a5Type argument 5 class
     * @param a6Type argument 6 class
     * @param a7Type argument 7 class
     * @param a8Type argument 8 class
     * @param a9type argument 9 class
     * @param  Class type
     * @param  argument 1 type
     * @param  argument 2 type
     * @param  argument 3 type
     * @param  argument 4 type
     * @param  argument 5 type
     * @param  argument 6 type
     * @param  argument 7 type
     * @param  argument 8 type
     * @param  argument 9 type
     * @return function handle
     */
    public static  R9 loadConstuctor9(
                                                                                    final Class clazz,
                                                                                    boolean setAccessible,
                                                                                    Class a1Type,
                                                                                    Class a2Type,
                                                                                    Class a3Type,
                                                                                    Class a4Type,
                                                                                    Class a5Type,
                                                                                    Class a6Type,
                                                                                    Class a7Type,
                                                                                    Class a8Type,
                                                                                    Class a9type) {
        final Constructor constructor=
            getDeclaredConstructor(clazz, setAccessible, a1Type, a2Type, a3Type, a4Type, a5Type, a6Type, a7Type, a8Type, a9type);
        return new R9(){
            public C call(A1 a1,A2 a2,A3 a3,A4 a4,A5 a5,A6 a6,A7 a7,A8 a8,A9 a9){
                return newInstance(constructor,a1,a2,a3,a4,a5,a6,a7,a8,a9);
            }
        };
    }

    public static  T getStaticField(Class clazz, String name, Class type) {
        R1 caster = makeCaster(type);
        try {
            return caster.call(clazz.getField(name).get(null));
        } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
            throw new ReflectException(e);
        }
    }

    public static void setStaticField(Class clazz, String name, boolean setAccessible, final Object value) {
        try {
            Field field = clazz.getDeclaredField(name);
            if (setAccessible) {
                field.setAccessible(true);
            }
            field.set(null, value);
        } catch (NoSuchFieldException | SecurityException | IllegalAccessException e) {
            throw new ReflectException(e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy