Java tutorial
/* * Copyright 2003,2004 The Apache Software Foundation * * Licensed 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 * * https://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.springframework.cglib.core; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.asm.Attribute; import org.springframework.asm.Type; /** * @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $ */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class ReflectUtils { private ReflectUtils() { } private static final Map primitives = new HashMap(8); private static final Map transforms = new HashMap(8); private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader(); // SPRING PATCH BEGIN private static final Method privateLookupInMethod; private static final Method lookupDefineClassMethod; private static final Method classLoaderDefineClassMethod; private static final ProtectionDomain PROTECTION_DOMAIN; private static final Throwable THROWABLE; private static final List<Method> OBJECT_METHODS = new ArrayList<Method>(); static { Method privateLookupIn; Method lookupDefineClass; Method classLoaderDefineClass; ProtectionDomain protectionDomain; Throwable throwable = null; try { privateLookupIn = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { try { return MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); } catch (NoSuchMethodException ex) { return null; } } }); lookupDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { try { return MethodHandles.Lookup.class.getMethod("defineClass", byte[].class); } catch (NoSuchMethodException ex) { return null; } } }); classLoaderDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { return ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class); } }); protectionDomain = getProtectionDomain(ReflectUtils.class); AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { Method[] methods = Object.class.getDeclaredMethods(); for (Method method : methods) { if ("finalize".equals(method.getName()) || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) { continue; } OBJECT_METHODS.add(method); } return null; } }); } catch (Throwable t) { privateLookupIn = null; lookupDefineClass = null; classLoaderDefineClass = null; protectionDomain = null; throwable = t; } privateLookupInMethod = privateLookupIn; lookupDefineClassMethod = lookupDefineClass; classLoaderDefineClassMethod = classLoaderDefineClass; PROTECTION_DOMAIN = protectionDomain; THROWABLE = throwable; } // SPRING PATCH END private static final String[] CGLIB_PACKAGES = { "java.lang", }; static { primitives.put("byte", Byte.TYPE); primitives.put("char", Character.TYPE); primitives.put("double", Double.TYPE); primitives.put("float", Float.TYPE); primitives.put("int", Integer.TYPE); primitives.put("long", Long.TYPE); primitives.put("short", Short.TYPE); primitives.put("boolean", Boolean.TYPE); transforms.put("byte", "B"); transforms.put("char", "C"); transforms.put("double", "D"); transforms.put("float", "F"); transforms.put("int", "I"); transforms.put("long", "J"); transforms.put("short", "S"); transforms.put("boolean", "Z"); } public static ProtectionDomain getProtectionDomain(final Class source) { if (source == null) { return null; } return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return source.getProtectionDomain(); } }); } public static Type[] getExceptionTypes(Member member) { if (member instanceof Method) { return TypeUtils.getTypes(((Method) member).getExceptionTypes()); } else if (member instanceof Constructor) { return TypeUtils.getTypes(((Constructor) member).getExceptionTypes()); } else { throw new IllegalArgumentException("Cannot get exception types of a field"); } } public static Signature getSignature(Member member) { if (member instanceof Method) { return new Signature(member.getName(), Type.getMethodDescriptor((Method) member)); } else if (member instanceof Constructor) { Type[] types = TypeUtils.getTypes(((Constructor) member).getParameterTypes()); return new Signature(Constants.CONSTRUCTOR_NAME, Type.getMethodDescriptor(Type.VOID_TYPE, types)); } else { throw new IllegalArgumentException("Cannot get signature of a field"); } } public static Constructor findConstructor(String desc) { return findConstructor(desc, defaultLoader); } public static Constructor findConstructor(String desc, ClassLoader loader) { try { int lparen = desc.indexOf('('); String className = desc.substring(0, lparen).trim(); return getClass(className, loader).getConstructor(parseTypes(desc, loader)); } catch (ClassNotFoundException | NoSuchMethodException ex) { throw new CodeGenerationException(ex); } } public static Method findMethod(String desc) { return findMethod(desc, defaultLoader); } public static Method findMethod(String desc, ClassLoader loader) { try { int lparen = desc.indexOf('('); int dot = desc.lastIndexOf('.', lparen); String className = desc.substring(0, dot).trim(); String methodName = desc.substring(dot + 1, lparen).trim(); return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader)); } catch (ClassNotFoundException | NoSuchMethodException ex) { throw new CodeGenerationException(ex); } } private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException { int lparen = desc.indexOf('('); int rparen = desc.indexOf(')', lparen); List params = new ArrayList(); int start = lparen + 1; for (;;) { int comma = desc.indexOf(',', start); if (comma < 0) { break; } params.add(desc.substring(start, comma).trim()); start = comma + 1; } if (start < rparen) { params.add(desc.substring(start, rparen).trim()); } Class[] types = new Class[params.size()]; for (int i = 0; i < types.length; i++) { types[i] = getClass((String) params.get(i), loader); } return types; } private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException { return getClass(className, loader, CGLIB_PACKAGES); } private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException { String save = className; int dimensions = 0; int index = 0; while ((index = className.indexOf("[]", index) + 1) > 0) { dimensions++; } StringBuffer brackets = new StringBuffer(className.length() - dimensions); for (int i = 0; i < dimensions; i++) { brackets.append('['); } className = className.substring(0, className.length() - 2 * dimensions); String prefix = (dimensions > 0) ? brackets + "L" : ""; String suffix = (dimensions > 0) ? ";" : ""; try { return Class.forName(prefix + className + suffix, false, loader); } catch (ClassNotFoundException ignore) { } for (int i = 0; i < packages.length; i++) { try { return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader); } catch (ClassNotFoundException ignore) { } } if (dimensions == 0) { Class c = (Class) primitives.get(className); if (c != null) { return c; } } else { String transform = (String) transforms.get(className); if (transform != null) { try { return Class.forName(brackets + transform, false, loader); } catch (ClassNotFoundException ignore) { } } } throw new ClassNotFoundException(save); } public static Object newInstance(Class type) { return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null); } public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) { return newInstance(getConstructor(type, parameterTypes), args); } @SuppressWarnings("deprecation") // on JDK 9 public static Object newInstance(final Constructor cstruct, final Object[] args) { boolean flag = cstruct.isAccessible(); try { if (!flag) { cstruct.setAccessible(true); } Object result = cstruct.newInstance(args); return result; } catch (InstantiationException e) { throw new CodeGenerationException(e); } catch (IllegalAccessException e) { throw new CodeGenerationException(e); } catch (InvocationTargetException e) { throw new CodeGenerationException(e.getTargetException()); } finally { if (!flag) { cstruct.setAccessible(flag); } } } public static Constructor getConstructor(Class type, Class[] parameterTypes) { try { Constructor constructor = type.getDeclaredConstructor(parameterTypes); constructor.setAccessible(true); return constructor; } catch (NoSuchMethodException e) { throw new CodeGenerationException(e); } } public static String[] getNames(Class[] classes) { if (classes == null) return null; String[] names = new String[classes.length]; for (int i = 0; i < names.length; i++) { names[i] = classes[i].getName(); } return names; } public static Class[] getClasses(Object[] objects) { Class[] classes = new Class[objects.length]; for (int i = 0; i < objects.length; i++) { classes[i] = objects[i].getClass(); } return classes; } public static Method findNewInstance(Class iface) { Method m = findInterfaceMethod(iface); if (!m.getName().equals("newInstance")) { throw new IllegalArgumentException(iface + " missing newInstance method"); } return m; } public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) { Set methods = new HashSet(); for (int i = 0; i < properties.length; i++) { PropertyDescriptor pd = properties[i]; if (read) { methods.add(pd.getReadMethod()); } if (write) { methods.add(pd.getWriteMethod()); } } methods.remove(null); return (Method[]) methods.toArray(new Method[methods.size()]); } public static PropertyDescriptor[] getBeanProperties(Class type) { return getPropertiesHelper(type, true, true); } public static PropertyDescriptor[] getBeanGetters(Class type) { return getPropertiesHelper(type, true, false); } public static PropertyDescriptor[] getBeanSetters(Class type) { return getPropertiesHelper(type, false, true); } private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) { try { BeanInfo info = Introspector.getBeanInfo(type, Object.class); PropertyDescriptor[] all = info.getPropertyDescriptors(); if (read && write) { return all; } List properties = new ArrayList(all.length); for (int i = 0; i < all.length; i++) { PropertyDescriptor pd = all[i]; if ((read && pd.getReadMethod() != null) || (write && pd.getWriteMethod() != null)) { properties.add(pd); } } return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]); } catch (IntrospectionException e) { throw new CodeGenerationException(e); } } public static Method findDeclaredMethod(final Class type, final String methodName, final Class[] parameterTypes) throws NoSuchMethodException { Class cl = type; while (cl != null) { try { return cl.getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { cl = cl.getSuperclass(); } } throw new NoSuchMethodException(methodName); } public static List addAllMethods(final Class type, final List list) { if (type == Object.class) { list.addAll(OBJECT_METHODS); } else list.addAll(java.util.Arrays.asList(type.getDeclaredMethods())); Class superclass = type.getSuperclass(); if (superclass != null) { addAllMethods(superclass, list); } Class[] interfaces = type.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { addAllMethods(interfaces[i], list); } return list; } public static List addAllInterfaces(Class type, List list) { Class superclass = type.getSuperclass(); if (superclass != null) { list.addAll(Arrays.asList(type.getInterfaces())); addAllInterfaces(superclass, list); } return list; } public static Method findInterfaceMethod(Class iface) { if (!iface.isInterface()) { throw new IllegalArgumentException(iface + " is not an interface"); } Method[] methods = iface.getDeclaredMethods(); if (methods.length != 1) { throw new IllegalArgumentException("expecting exactly 1 method in " + iface); } return methods[0]; } // SPRING PATCH BEGIN public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception { return defineClass(className, b, loader, null, null); } public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain) throws Exception { return defineClass(className, b, loader, protectionDomain, null); } @SuppressWarnings("deprecation") // on JDK 9 public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception { Class c = null; // Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches if (contextClass != null && contextClass.getClassLoader() == loader && privateLookupInMethod != null && lookupDefineClassMethod != null) { try { MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass, MethodHandles.lookup()); c = (Class) lookupDefineClassMethod.invoke(lookup, b); } catch (InvocationTargetException ex) { Throwable target = ex.getTargetException(); if (target.getClass() != LinkageError.class && target.getClass() != IllegalArgumentException.class) { throw new CodeGenerationException(target); } // in case of plain LinkageError (class already defined) // or IllegalArgumentException (class in different package): // fall through to traditional ClassLoader.defineClass below } catch (Throwable ex) { throw new CodeGenerationException(ex); } } // Classic option: protected ClassLoader.defineClass method if (c == null && classLoaderDefineClassMethod != null) { if (protectionDomain == null) { protectionDomain = PROTECTION_DOMAIN; } Object[] args = new Object[] { className, b, 0, b.length, protectionDomain }; try { if (!classLoaderDefineClassMethod.isAccessible()) { classLoaderDefineClassMethod.setAccessible(true); } c = (Class) classLoaderDefineClassMethod.invoke(loader, args); } catch (InvocationTargetException ex) { throw new CodeGenerationException(ex.getTargetException()); } catch (Throwable ex) { // Fall through if setAccessible fails with InaccessibleObjectException on JDK 9+ // (on the module path and/or with a JVM bootstrapped with --illegal-access=deny) if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) { throw new CodeGenerationException(ex); } } } // Fallback option: JDK 9+ Lookup.defineClass API even if ClassLoader does not match if (c == null && contextClass != null && contextClass.getClassLoader() != loader && privateLookupInMethod != null && lookupDefineClassMethod != null) { try { MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass, MethodHandles.lookup()); c = (Class) lookupDefineClassMethod.invoke(lookup, b); } catch (InvocationTargetException ex) { throw new CodeGenerationException(ex.getTargetException()); } catch (Throwable ex) { throw new CodeGenerationException(ex); } } // No defineClass variant available at all? if (c == null) { throw new CodeGenerationException(THROWABLE); } // Force static initializers to run. Class.forName(className, true, loader); return c; } // SPRING PATCH END public static int findPackageProtected(Class[] classes) { for (int i = 0; i < classes.length; i++) { if (!Modifier.isPublic(classes[i].getModifiers())) { return i; } } return 0; } public static MethodInfo getMethodInfo(final Member member, final int modifiers) { final Signature sig = getSignature(member); return new MethodInfo() { private ClassInfo ci; public ClassInfo getClassInfo() { if (ci == null) ci = ReflectUtils.getClassInfo(member.getDeclaringClass()); return ci; } public int getModifiers() { return modifiers; } public Signature getSignature() { return sig; } public Type[] getExceptionTypes() { return ReflectUtils.getExceptionTypes(member); } public Attribute getAttribute() { return null; } }; } public static MethodInfo getMethodInfo(Member member) { return getMethodInfo(member, member.getModifiers()); } public static ClassInfo getClassInfo(final Class clazz) { final Type type = Type.getType(clazz); final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass()); return new ClassInfo() { public Type getType() { return type; } public Type getSuperType() { return sc; } public Type[] getInterfaces() { return TypeUtils.getTypes(clazz.getInterfaces()); } public int getModifiers() { return clazz.getModifiers(); } }; } // used by MethodInterceptorGenerated generated code public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) { Map map = new HashMap(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; map.put(method.getName() + Type.getMethodDescriptor(method), method); } Method[] result = new Method[namesAndDescriptors.length / 2]; for (int i = 0; i < result.length; i++) { result[i] = (Method) map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]); if (result[i] == null) { // TODO: error? } } return result; } }