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.management.ManagementFactory; 025import java.lang.management.ThreadInfo; 026import java.lang.management.ThreadMXBean; 027import java.lang.reflect.Constructor; 028import java.lang.reflect.InvocationTargetException; 029import java.lang.reflect.Method; 030import java.nio.charset.Charset; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.slf4j.Logger; 033 034@InterfaceAudience.Private 035public class ReflectionUtils { 036 @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) 037 public static <T> T instantiateWithCustomCtor(String className, Class<?>[] ctorArgTypes, 038 Object[] ctorArgs) { 039 try { 040 Class<? extends T> resultType = (Class<? extends T>) Class.forName(className); 041 Constructor<? extends T> ctor = resultType.getDeclaredConstructor(ctorArgTypes); 042 return instantiate(className, ctor, ctorArgs); 043 } catch (ClassNotFoundException e) { 044 throw new UnsupportedOperationException("Unable to find " + className, e); 045 } catch (NoSuchMethodException e) { 046 throw new UnsupportedOperationException( 047 "Unable to find suitable constructor for class " + className, e); 048 } 049 } 050 051 public static <T> T instantiate(final String className, Constructor<T> ctor, Object... ctorArgs) { 052 try { 053 ctor.setAccessible(true); 054 return ctor.newInstance(ctorArgs); 055 } catch (IllegalAccessException e) { 056 throw new UnsupportedOperationException("Unable to access specified class " + className, e); 057 } catch (InstantiationException e) { 058 throw new UnsupportedOperationException("Unable to instantiate specified class " + className, 059 e); 060 } catch (InvocationTargetException e) { 061 throw new UnsupportedOperationException("Constructor threw an exception for " + className, e); 062 } 063 } 064 065 @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) 066 public static <T> T newInstance(String className, Object... params) { 067 Class<T> type; 068 try { 069 type = (Class<T>) getClassLoader().loadClass(className); 070 } catch (ClassNotFoundException | ClassCastException e) { 071 throw new UnsupportedOperationException("Unable to load specified class " + className, e); 072 } 073 return instantiate(type.getName(), findConstructor(type, params), params); 074 } 075 076 public static ClassLoader getClassLoader() { 077 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 078 if (cl == null) { 079 cl = ReflectionUtils.class.getClassLoader(); 080 } 081 if (cl == null) { 082 cl = ClassLoader.getSystemClassLoader(); 083 } 084 if (cl == null) { 085 throw new RuntimeException("A ClassLoader could not be found"); 086 } 087 return cl; 088 } 089 090 public static <T> T newInstance(Class<T> type, Object... params) { 091 return instantiate(type.getName(), findConstructor(type, params), params); 092 } 093 094 @SuppressWarnings("unchecked") 095 public static <T> Constructor<T> findConstructor(Class<T> type, Object... paramTypes) { 096 Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors(); 097 for (Constructor<T> ctor : constructors) { 098 Class<?>[] ctorParamTypes = ctor.getParameterTypes(); 099 if (ctorParamTypes.length != paramTypes.length) { 100 continue; 101 } 102 103 boolean match = true; 104 for (int i = 0; i < ctorParamTypes.length && match; ++i) { 105 if (paramTypes[i] == null) { 106 match = !ctorParamTypes[i].isPrimitive(); 107 } else { 108 Class<?> paramType = paramTypes[i].getClass(); 109 match = !ctorParamTypes[i].isPrimitive() 110 ? ctorParamTypes[i].isAssignableFrom(paramType) 111 : ((int.class.equals(ctorParamTypes[i]) && Integer.class.equals(paramType)) 112 || (long.class.equals(ctorParamTypes[i]) && Long.class.equals(paramType)) 113 || (double.class.equals(ctorParamTypes[i]) && Double.class.equals(paramType)) 114 || (char.class.equals(ctorParamTypes[i]) && Character.class.equals(paramType)) 115 || (short.class.equals(ctorParamTypes[i]) && Short.class.equals(paramType)) 116 || (boolean.class.equals(ctorParamTypes[i]) && Boolean.class.equals(paramType)) 117 || (byte.class.equals(ctorParamTypes[i]) && Byte.class.equals(paramType))); 118 } 119 } 120 121 if (match) { 122 return ctor; 123 } 124 } 125 throw new UnsupportedOperationException( 126 "Unable to find suitable constructor for class " + type.getName()); 127 } 128 129 /* synchronized on ReflectionUtils.class */ 130 private static long previousLogTime = 0; 131 private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); 132 133 /** 134 * Log the current thread stacks at INFO level. 135 * @param log the logger that logs the stack trace 136 * @param title a descriptive title for the call stacks 137 * @param minInterval the minimum time from the last 138 */ 139 public static void logThreadInfo(Logger log, String title, long minInterval) { 140 boolean dumpStack = false; 141 if (log.isInfoEnabled()) { 142 synchronized (ReflectionUtils.class) { 143 long now = EnvironmentEdgeManager.currentTime(); 144 if (now - previousLogTime >= minInterval * 1000) { 145 previousLogTime = now; 146 dumpStack = true; 147 } 148 } 149 if (dumpStack) { 150 try { 151 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 152 printThreadInfo(new PrintStream(buffer, false, "UTF-8"), title); 153 log.info(buffer.toString(Charset.defaultCharset().name())); 154 } catch (UnsupportedEncodingException ignored) { 155 log.warn( 156 "Could not write thread info about '" + title + "' due to a string encoding issue."); 157 } 158 } 159 } 160 } 161 162 /** 163 * Print all of the thread's information and stack traces. 164 * @param stream the stream to 165 * @param title a string title for the stack trace 166 */ 167 static void printThreadInfo(PrintStream stream, String title) { 168 final int STACK_DEPTH = 20; 169 boolean contention = threadBean.isThreadContentionMonitoringEnabled(); 170 long[] threadIds = threadBean.getAllThreadIds(); 171 stream.println("Process Thread Dump: " + title); 172 stream.println(threadIds.length + " active threads"); 173 for (long tid : threadIds) { 174 ThreadInfo info = threadBean.getThreadInfo(tid, STACK_DEPTH); 175 if (info == null) { 176 stream.println(" Inactive"); 177 continue; 178 } 179 stream.println("Thread " + getTaskName(info.getThreadId(), info.getThreadName()) + ":"); 180 Thread.State state = info.getThreadState(); 181 stream.println(" State: " + state); 182 stream.println(" Blocked count: " + info.getBlockedCount()); 183 stream.println(" Waited count: " + info.getWaitedCount()); 184 if (contention) { 185 stream.println(" Blocked time: " + info.getBlockedTime()); 186 stream.println(" Waited time: " + info.getWaitedTime()); 187 } 188 if (state == Thread.State.WAITING) { 189 stream.println(" Waiting on " + info.getLockName()); 190 } else if (state == Thread.State.BLOCKED) { 191 stream.println(" Blocked on " + info.getLockName()); 192 stream 193 .println(" Blocked by " + getTaskName(info.getLockOwnerId(), info.getLockOwnerName())); 194 } 195 stream.println(" Stack:"); 196 for (StackTraceElement frame : info.getStackTrace()) { 197 stream.println(" " + frame.toString()); 198 } 199 } 200 stream.flush(); 201 } 202 203 private static String getTaskName(long id, String name) { 204 if (name == null) { 205 return Long.toString(id); 206 } 207 return id + " (" + name + ")"; 208 } 209 210 /** 211 * Get and invoke the target method from the given object with given parameters 212 * @param obj the object to get and invoke method from 213 * @param methodName the name of the method to invoke 214 * @param params the parameters for the method to invoke 215 * @return the return value of the method invocation 216 */ 217 @NonNull 218 public static Object invokeMethod(Object obj, String methodName, Object... params) { 219 Method m; 220 try { 221 m = obj.getClass().getMethod(methodName, getParameterTypes(params)); 222 m.setAccessible(true); 223 return m.invoke(obj, params); 224 } catch (NoSuchMethodException e) { 225 throw new UnsupportedOperationException("Cannot find specified method " + methodName, e); 226 } catch (IllegalAccessException e) { 227 throw new UnsupportedOperationException("Unable to access specified method " + methodName, e); 228 } catch (IllegalArgumentException e) { 229 throw new UnsupportedOperationException("Illegal arguments supplied for method " + methodName, 230 e); 231 } catch (InvocationTargetException e) { 232 throw new UnsupportedOperationException("Method threw an exception for " + methodName, e); 233 } 234 } 235 236 private static Class<?>[] getParameterTypes(Object[] params) { 237 Class<?>[] parameterTypes = new Class<?>[params.length]; 238 for (int i = 0; i < params.length; i++) { 239 parameterTypes[i] = params[i].getClass(); 240 } 241 return parameterTypes; 242 } 243 244}