001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util; 018 019import java.lang.reflect.Field; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022import java.util.Arrays; 023 024/** 025 * Helper for working with reflection on classes. 026 * <p/> 027 * This code is based on org.apache.camel.spring.util.ReflectionUtils class. 028 */ 029public final class ReflectionHelper { 030 031 private ReflectionHelper() { 032 // utility class 033 } 034 035 /** 036 * Callback interface invoked on each field in the hierarchy. 037 */ 038 @FunctionalInterface 039 public interface FieldCallback { 040 041 /** 042 * Perform an operation using the given field. 043 * 044 * @param field the field to operate on 045 */ 046 void doWith(Field field) throws IllegalArgumentException, IllegalAccessException; 047 } 048 049 /** 050 * Action to take on each method. 051 */ 052 @FunctionalInterface 053 public interface MethodCallback { 054 055 /** 056 * Perform an operation using the given method. 057 * 058 * @param method the method to operate on 059 */ 060 void doWith(Method method) throws IllegalArgumentException, IllegalAccessException; 061 } 062 063 /** 064 * Action to take on each class. 065 */ 066 @FunctionalInterface 067 public interface ClassCallback { 068 069 /** 070 * Perform an operation using the given class. 071 * 072 * @param clazz the class to operate on 073 */ 074 void doWith(Class clazz) throws IllegalArgumentException, IllegalAccessException; 075 } 076 077 /** 078 * Perform the given callback operation on the nested (inner) classes. 079 * 080 * @param clazz class to start looking at 081 * @param cc the callback to invoke for each inner class (excluding the class itself) 082 */ 083 public static void doWithClasses(Class<?> clazz, ClassCallback cc) throws IllegalArgumentException { 084 // and then nested classes 085 Class[] classes = clazz.getDeclaredClasses(); 086 for (Class aClazz : classes) { 087 try { 088 cc.doWith(aClazz); 089 } catch (IllegalAccessException ex) { 090 throw new IllegalStateException("Shouldn't be illegal to access class '" + aClazz.getName() + "': " + ex); 091 } 092 } 093 } 094 095 /** 096 * Invoke the given callback on all fields in the target class, going up the class hierarchy to get all declared 097 * fields. 098 * 099 * @param clazz the target class to analyze 100 * @param fc the callback to invoke for each field 101 */ 102 public static void doWithFields(Class<?> clazz, FieldCallback fc) throws IllegalArgumentException { 103 // Keep backing up the inheritance hierarchy. 104 Class<?> targetClass = clazz; 105 do { 106 Field[] fields = targetClass.getDeclaredFields(); 107 for (Field field : fields) { 108 try { 109 fc.doWith(field); 110 } catch (IllegalAccessException ex) { 111 throw new IllegalStateException("Shouldn't be illegal to access field '" + field.getName() + "': " + ex); 112 } 113 } 114 targetClass = targetClass.getSuperclass(); 115 } while (targetClass != null && targetClass != Object.class); 116 } 117 118 /** 119 * Perform the given callback operation on all matching methods of the given class and superclasses (or given 120 * interface and super-interfaces). 121 * <p/> 122 * <b>Important:</b> This method does not take the {@link java.lang.reflect.Method#isBridge() bridge methods} into 123 * account. 124 * 125 * @param clazz class to start looking at 126 * @param mc the callback to invoke for each method 127 */ 128 public static void doWithMethods(Class<?> clazz, MethodCallback mc) throws IllegalArgumentException { 129 // Keep backing up the inheritance hierarchy. 130 Method[] methods = clazz.getDeclaredMethods(); 131 for (Method method : methods) { 132 if (method.isBridge()) { 133 // skip the bridge methods which in Java 8 leads to problems with inheritance 134 // see https://bugs.openjdk.java.net/browse/JDK-6695379 135 continue; 136 } 137 try { 138 mc.doWith(method); 139 } catch (IllegalAccessException ex) { 140 throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName() + "': " + ex); 141 } 142 } 143 if (clazz.getSuperclass() != null) { 144 doWithMethods(clazz.getSuperclass(), mc); 145 } else if (clazz.isInterface()) { 146 for (Class<?> superIfc : clazz.getInterfaces()) { 147 doWithMethods(superIfc, mc); 148 } 149 } 150 } 151 152 /** 153 * Attempt to find a {@link Method} on the supplied class with the supplied name and parameter types. Searches all 154 * superclasses up to {@code Object}. 155 * <p> 156 * Returns {@code null} if no {@link Method} can be found. 157 * 158 * @param clazz the class to introspect 159 * @param name the name of the method 160 * @param paramTypes the parameter types of the method (may be {@code null} to indicate any signature) 161 * @return the Method object, or {@code null} if none found 162 */ 163 public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { 164 ObjectHelper.notNull(clazz, "Class must not be null"); 165 ObjectHelper.notNull(name, "Method name must not be null"); 166 Class<?> searchType = clazz; 167 while (searchType != null) { 168 Method[] methods = searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods(); 169 for (Method method : methods) { 170 if (name.equals(method.getName()) 171 && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { 172 return method; 173 } 174 } 175 searchType = searchType.getSuperclass(); 176 } 177 return null; 178 } 179 180 public static void setField(Field f, Object instance, Object value) { 181 try { 182 boolean oldAccessible = f.isAccessible(); 183 boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible; 184 if (shouldSetAccessible) { 185 f.setAccessible(true); 186 } 187 f.set(instance, value); 188 if (shouldSetAccessible) { 189 f.setAccessible(oldAccessible); 190 } 191 } catch (Exception ex) { 192 throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + f); 193 } 194 } 195 196 public static Object getField(Field f, Object instance) { 197 try { 198 boolean oldAccessible = f.isAccessible(); 199 boolean shouldSetAccessible = !Modifier.isPublic(f.getModifiers()) && !oldAccessible; 200 if (shouldSetAccessible) { 201 f.setAccessible(true); 202 } 203 Object answer = f.get(instance); 204 if (shouldSetAccessible) { 205 f.setAccessible(oldAccessible); 206 } 207 return answer; 208 } catch (Exception ex) { 209 // ignore 210 } 211 return null; 212 } 213 214}