001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import edu.umd.cs.findbugs.annotations.NonNull; 021import java.io.ByteArrayOutputStream; 022import java.io.PrintStream; 023import java.io.UnsupportedEncodingException; 024import java.lang.invoke.CallSite; 025import java.lang.invoke.LambdaMetafactory; 026import java.lang.invoke.MethodHandle; 027import java.lang.invoke.MethodHandles; 028import java.lang.invoke.MethodType; 029import java.lang.management.ManagementFactory; 030import java.lang.management.ThreadInfo; 031import java.lang.management.ThreadMXBean; 032import java.lang.reflect.Constructor; 033import java.lang.reflect.Field; 034import java.lang.reflect.InvocationTargetException; 035import java.lang.reflect.Method; 036import java.nio.charset.Charset; 037import java.util.function.Function; 038import org.apache.yetus.audience.InterfaceAudience; 039import org.slf4j.Logger; 040 041@InterfaceAudience.Private 042public class ReflectionUtils { 043 @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) 044 public static <T> T instantiateWithCustomCtor(String className, Class<?>[] ctorArgTypes, 045 Object[] ctorArgs) { 046 try { 047 Class<? extends T> resultType = (Class<? extends T>) Class.forName(className); 048 Constructor<? extends T> ctor = resultType.getDeclaredConstructor(ctorArgTypes); 049 return instantiate(className, ctor, ctorArgs); 050 } catch (ClassNotFoundException e) { 051 throw new UnsupportedOperationException("Unable to find " + className, e); 052 } catch (NoSuchMethodException e) { 053 throw new UnsupportedOperationException( 054 "Unable to find suitable constructor for class " + className, e); 055 } 056 } 057 058 public static <T> T instantiate(final String className, Constructor<T> ctor, Object... ctorArgs) { 059 try { 060 ctor.setAccessible(true); 061 return ctor.newInstance(ctorArgs); 062 } catch (IllegalAccessException e) { 063 throw new UnsupportedOperationException("Unable to access specified class " + className, e); 064 } catch (InstantiationException e) { 065 throw new UnsupportedOperationException("Unable to instantiate specified class " + className, 066 e); 067 } catch (InvocationTargetException e) { 068 throw new UnsupportedOperationException("Constructor threw an exception for " + className, e); 069 } 070 } 071 072 @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) 073 public static <T> T newInstance(String className, Object... params) { 074 Class<T> type; 075 try { 076 type = (Class<T>) getClassLoader().loadClass(className); 077 } catch (ClassNotFoundException | ClassCastException e) { 078 throw new UnsupportedOperationException("Unable to load specified class " + className, e); 079 } 080 return instantiate(type.getName(), findConstructor(type, params), params); 081 } 082 083 public static ClassLoader getClassLoader() { 084 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 085 if (cl == null) { 086 cl = ReflectionUtils.class.getClassLoader(); 087 } 088 if (cl == null) { 089 cl = ClassLoader.getSystemClassLoader(); 090 } 091 if (cl == null) { 092 throw new RuntimeException("A ClassLoader could not be found"); 093 } 094 return cl; 095 } 096 097 public static <T> T newInstance(Class<T> type, Object... params) { 098 return instantiate(type.getName(), findConstructor(type, params), params); 099 } 100 101 @SuppressWarnings("unchecked") 102 public static <T> Constructor<T> findConstructor(Class<T> type, Object... paramTypes) { 103 Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors(); 104 for (Constructor<T> ctor : constructors) { 105 Class<?>[] ctorParamTypes = ctor.getParameterTypes(); 106 if (ctorParamTypes.length != paramTypes.length) { 107 continue; 108 } 109 110 boolean match = true; 111 for (int i = 0; i < ctorParamTypes.length && match; ++i) { 112 if (paramTypes[i] == null) { 113 match = !ctorParamTypes[i].isPrimitive(); 114 } else { 115 Class<?> paramType = paramTypes[i].getClass(); 116 match = !ctorParamTypes[i].isPrimitive() 117 ? ctorParamTypes[i].isAssignableFrom(paramType) 118 : ((int.class.equals(ctorParamTypes[i]) && Integer.class.equals(paramType)) 119 || (long.class.equals(ctorParamTypes[i]) && Long.class.equals(paramType)) 120 || (double.class.equals(ctorParamTypes[i]) && Double.class.equals(paramType)) 121 || (char.class.equals(ctorParamTypes[i]) && Character.class.equals(paramType)) 122 || (short.class.equals(ctorParamTypes[i]) && Short.class.equals(paramType)) 123 || (boolean.class.equals(ctorParamTypes[i]) && Boolean.class.equals(paramType)) 124 || (byte.class.equals(ctorParamTypes[i]) && Byte.class.equals(paramType))); 125 } 126 } 127 128 if (match) { 129 return ctor; 130 } 131 } 132 throw new UnsupportedOperationException( 133 "Unable to find suitable constructor for class " + type.getName()); 134 } 135 136 /* synchronized on ReflectionUtils.class */ 137 private static long previousLogTime = 0; 138 private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); 139 140 /** 141 * Log the current thread stacks at INFO level. 142 * @param log the logger that logs the stack trace 143 * @param title a descriptive title for the call stacks 144 * @param minInterval the minimum time from the last 145 */ 146 public static void logThreadInfo(Logger log, String title, long minInterval) { 147 boolean dumpStack = false; 148 if (log.isInfoEnabled()) { 149 synchronized (ReflectionUtils.class) { 150 long now = EnvironmentEdgeManager.currentTime(); 151 if (now - previousLogTime >= minInterval * 1000) { 152 previousLogTime = now; 153 dumpStack = true; 154 } 155 } 156 if (dumpStack) { 157 try { 158 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 159 printThreadInfo(new PrintStream(buffer, false, "UTF-8"), title); 160 log.info(buffer.toString(Charset.defaultCharset().name())); 161 } catch (UnsupportedEncodingException ignored) { 162 log.warn( 163 "Could not write thread info about '" + title + "' due to a string encoding issue."); 164 } 165 } 166 } 167 } 168 169 /** 170 * Print all of the thread's information and stack traces. 171 * @param stream the stream to 172 * @param title a string title for the stack trace 173 */ 174 static void printThreadInfo(PrintStream stream, String title) { 175 final int STACK_DEPTH = 20; 176 boolean contention = threadBean.isThreadContentionMonitoringEnabled(); 177 long[] threadIds = threadBean.getAllThreadIds(); 178 stream.println("Process Thread Dump: " + title); 179 stream.println(threadIds.length + " active threads"); 180 for (long tid : threadIds) { 181 ThreadInfo info = threadBean.getThreadInfo(tid, STACK_DEPTH); 182 if (info == null) { 183 stream.println(" Inactive"); 184 continue; 185 } 186 stream.println("Thread " + getTaskName(info.getThreadId(), info.getThreadName()) + ":"); 187 Thread.State state = info.getThreadState(); 188 stream.println(" State: " + state); 189 stream.println(" Blocked count: " + info.getBlockedCount()); 190 stream.println(" Waited count: " + info.getWaitedCount()); 191 if (contention) { 192 stream.println(" Blocked time: " + info.getBlockedTime()); 193 stream.println(" Waited time: " + info.getWaitedTime()); 194 } 195 if (state == Thread.State.WAITING) { 196 stream.println(" Waiting on " + info.getLockName()); 197 } else if (state == Thread.State.BLOCKED) { 198 stream.println(" Blocked on " + info.getLockName()); 199 stream 200 .println(" Blocked by " + getTaskName(info.getLockOwnerId(), info.getLockOwnerName())); 201 } 202 stream.println(" Stack:"); 203 for (StackTraceElement frame : info.getStackTrace()) { 204 stream.println(" " + frame.toString()); 205 } 206 } 207 stream.flush(); 208 } 209 210 private static String getTaskName(long id, String name) { 211 if (name == null) { 212 return Long.toString(id); 213 } 214 return id + " (" + name + ")"; 215 } 216 217 /** 218 * Creates a Function which can be called to performantly execute a reflected static method. The 219 * creation of the Function itself may not be fast, but executing that method thereafter should be 220 * much faster than {@link #invokeMethod(Object, String, Object...)}. 221 * @param lookupClazz the class to find the static method in 222 * @param methodName the method name 223 * @param argumentClazz the type of the argument 224 * @param returnValueClass the type of the return value 225 * @return a function which when called executes the requested static method. 226 * @throws Throwable exception types from the underlying reflection 227 */ 228 public static <I, R> Function<I, R> getOneArgStaticMethodAsFunction(Class<?> lookupClazz, 229 String methodName, Class<I> argumentClazz, Class<R> returnValueClass) throws Throwable { 230 MethodHandles.Lookup lookup = MethodHandles.lookup(); 231 MethodHandle methodHandle = lookup.findStatic(lookupClazz, methodName, 232 MethodType.methodType(returnValueClass, argumentClazz)); 233 CallSite site = 234 LambdaMetafactory.metafactory(lookup, "apply", MethodType.methodType(Function.class), 235 methodHandle.type().generic(), methodHandle, methodHandle.type()); 236 237 return (Function<I, R>) site.getTarget().invokeExact(); 238 239 } 240 241 /** 242 * Get and invoke the target method from the given object with given parameters 243 * @param obj the object to get and invoke method from 244 * @param methodName the name of the method to invoke 245 * @param params the parameters for the method to invoke 246 * @return the return value of the method invocation 247 */ 248 @NonNull 249 public static Object invokeMethod(Object obj, String methodName, Object... params) { 250 Method m; 251 try { 252 m = obj.getClass().getMethod(methodName, getParameterTypes(params)); 253 m.setAccessible(true); 254 return m.invoke(obj, params); 255 } catch (NoSuchMethodException e) { 256 throw new UnsupportedOperationException("Cannot find specified method " + methodName, e); 257 } catch (IllegalAccessException e) { 258 throw new UnsupportedOperationException("Unable to access specified method " + methodName, e); 259 } catch (IllegalArgumentException e) { 260 throw new UnsupportedOperationException("Illegal arguments supplied for method " + methodName, 261 e); 262 } catch (InvocationTargetException e) { 263 throw new UnsupportedOperationException("Method threw an exception for " + methodName, e); 264 } 265 } 266 267 private static Class<?>[] getParameterTypes(Object[] params) { 268 Class<?>[] parameterTypes = new Class<?>[params.length]; 269 for (int i = 0; i < params.length; i++) { 270 parameterTypes[i] = params[i].getClass(); 271 } 272 return parameterTypes; 273 } 274 275 public static Field getModifiersField() throws IllegalAccessException, NoSuchFieldException { 276 // this is copied from https://github.com/powermock/powermock/pull/1010/files to work around 277 // JDK 12+ 278 Field modifiersField = null; 279 try { 280 modifiersField = Field.class.getDeclaredField("modifiers"); 281 } catch (NoSuchFieldException e) { 282 try { 283 Method getDeclaredFields0 = 284 Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); 285 boolean accessibleBeforeSet = getDeclaredFields0.isAccessible(); 286 getDeclaredFields0.setAccessible(true); 287 Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); 288 getDeclaredFields0.setAccessible(accessibleBeforeSet); 289 for (Field field : fields) { 290 if ("modifiers".equals(field.getName())) { 291 modifiersField = field; 292 break; 293 } 294 } 295 if (modifiersField == null) { 296 throw e; 297 } 298 } catch (NoSuchMethodException ex) { 299 e.addSuppressed(ex); 300 throw e; 301 } catch (InvocationTargetException ex) { 302 e.addSuppressed(ex); 303 throw e; 304 } 305 } 306 return modifiersField; 307 } 308 309}