001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 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; 031 032import org.apache.yetus.audience.InterfaceAudience; 033import org.slf4j.Logger; 034 035import edu.umd.cs.findbugs.annotations.NonNull; 036 037@InterfaceAudience.Private 038public class ReflectionUtils { 039 @SuppressWarnings("unchecked") 040 public static <T> T instantiateWithCustomCtor(String className, 041 Class<? >[] ctorArgTypes, Object[] ctorArgs) { 042 try { 043 Class<? extends T> resultType = (Class<? extends T>) Class.forName(className); 044 Constructor<? extends T> ctor = resultType.getDeclaredConstructor(ctorArgTypes); 045 return instantiate(className, ctor, ctorArgs); 046 } catch (ClassNotFoundException e) { 047 throw new UnsupportedOperationException( 048 "Unable to find " + className, e); 049 } catch (NoSuchMethodException e) { 050 throw new UnsupportedOperationException( 051 "Unable to find suitable constructor for class " + className, e); 052 } 053 } 054 055 private static <T> T instantiate(final String className, Constructor<T> ctor, Object[] ctorArgs) { 056 try { 057 ctor.setAccessible(true); 058 return ctor.newInstance(ctorArgs); 059 } catch (IllegalAccessException e) { 060 throw new UnsupportedOperationException( 061 "Unable to access specified class " + className, e); 062 } catch (InstantiationException e) { 063 throw new UnsupportedOperationException( 064 "Unable to instantiate specified class " + className, e); 065 } catch (InvocationTargetException e) { 066 throw new UnsupportedOperationException( 067 "Constructor threw an exception for " + className, e); 068 } 069 } 070 071 public static <T> T newInstance(Class<T> type, Object... params) { 072 return instantiate(type.getName(), findConstructor(type, params), params); 073 } 074 075 @SuppressWarnings("unchecked") 076 public static <T> Constructor<T> findConstructor(Class<T> type, Object... paramTypes) { 077 Constructor<T>[] constructors = (Constructor<T>[]) type.getDeclaredConstructors(); 078 for (Constructor<T> ctor : constructors) { 079 Class<?>[] ctorParamTypes = ctor.getParameterTypes(); 080 if (ctorParamTypes.length != paramTypes.length) { 081 continue; 082 } 083 084 boolean match = true; 085 for (int i = 0; i < ctorParamTypes.length && match; ++i) { 086 Class<?> paramType = paramTypes[i].getClass(); 087 match = (!ctorParamTypes[i].isPrimitive()) ? ctorParamTypes[i].isAssignableFrom(paramType) : 088 ((int.class.equals(ctorParamTypes[i]) && Integer.class.equals(paramType)) || 089 (long.class.equals(ctorParamTypes[i]) && Long.class.equals(paramType)) || 090 (double.class.equals(ctorParamTypes[i]) && Double.class.equals(paramType)) || 091 (char.class.equals(ctorParamTypes[i]) && Character.class.equals(paramType)) || 092 (short.class.equals(ctorParamTypes[i]) && Short.class.equals(paramType)) || 093 (boolean.class.equals(ctorParamTypes[i]) && Boolean.class.equals(paramType)) || 094 (byte.class.equals(ctorParamTypes[i]) && Byte.class.equals(paramType))); 095 } 096 097 if (match) { 098 return ctor; 099 } 100 } 101 throw new UnsupportedOperationException( 102 "Unable to find suitable constructor for class " + type.getName()); 103 } 104 105 /* synchronized on ReflectionUtils.class */ 106 private static long previousLogTime = 0; 107 private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); 108 109 /** 110 * Log the current thread stacks at INFO level. 111 * @param log the logger that logs the stack trace 112 * @param title a descriptive title for the call stacks 113 * @param minInterval the minimum time from the last 114 */ 115 public static void logThreadInfo(Logger log, 116 String title, 117 long minInterval) { 118 boolean dumpStack = false; 119 if (log.isInfoEnabled()) { 120 synchronized (ReflectionUtils.class) { 121 long now = System.currentTimeMillis(); 122 if (now - previousLogTime >= minInterval * 1000) { 123 previousLogTime = now; 124 dumpStack = true; 125 } 126 } 127 if (dumpStack) { 128 try { 129 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 130 printThreadInfo(new PrintStream(buffer, false, "UTF-8"), title); 131 log.info(buffer.toString(Charset.defaultCharset().name())); 132 } catch (UnsupportedEncodingException ignored) { 133 log.warn("Could not write thread info about '" + title + 134 "' due to a string encoding issue."); 135 } 136 } 137 } 138 } 139 140 /** 141 * Print all of the thread's information and stack traces. 142 * 143 * @param stream the stream to 144 * @param title a string title for the stack trace 145 */ 146 private static void printThreadInfo(PrintStream stream, 147 String title) { 148 final int STACK_DEPTH = 20; 149 boolean contention = threadBean.isThreadContentionMonitoringEnabled(); 150 long[] threadIds = threadBean.getAllThreadIds(); 151 stream.println("Process Thread Dump: " + title); 152 stream.println(threadIds.length + " active threads"); 153 for (long tid: threadIds) { 154 ThreadInfo info = threadBean.getThreadInfo(tid, STACK_DEPTH); 155 if (info == null) { 156 stream.println(" Inactive"); 157 continue; 158 } 159 stream.println("Thread " + 160 getTaskName(info.getThreadId(), 161 info.getThreadName()) + ":"); 162 Thread.State state = info.getThreadState(); 163 stream.println(" State: " + state); 164 stream.println(" Blocked count: " + info.getBlockedCount()); 165 stream.println(" Waited count: " + info.getWaitedCount()); 166 if (contention) { 167 stream.println(" Blocked time: " + info.getBlockedTime()); 168 stream.println(" Waited time: " + info.getWaitedTime()); 169 } 170 if (state == Thread.State.WAITING) { 171 stream.println(" Waiting on " + info.getLockName()); 172 } else if (state == Thread.State.BLOCKED) { 173 stream.println(" Blocked on " + info.getLockName()); 174 stream.println(" Blocked by " + 175 getTaskName(info.getLockOwnerId(), 176 info.getLockOwnerName())); 177 } 178 stream.println(" Stack:"); 179 for (StackTraceElement frame: info.getStackTrace()) { 180 stream.println(" " + frame.toString()); 181 } 182 } 183 stream.flush(); 184 } 185 186 private static String getTaskName(long id, String name) { 187 if (name == null) { 188 return Long.toString(id); 189 } 190 return id + " (" + name + ")"; 191 } 192 193 /** 194 * Get and invoke the target method from the given object with given parameters 195 * @param obj the object to get and invoke method from 196 * @param methodName the name of the method to invoke 197 * @param params the parameters for the method to invoke 198 * @return the return value of the method invocation 199 */ 200 @NonNull 201 public static Object invokeMethod(Object obj, String methodName, Object... params) { 202 Method m; 203 try { 204 m = obj.getClass().getMethod(methodName, getParameterTypes(params)); 205 m.setAccessible(true); 206 return m.invoke(obj, params); 207 } catch (NoSuchMethodException e) { 208 throw new UnsupportedOperationException("Cannot find specified method " + methodName, e); 209 } catch (IllegalAccessException e) { 210 throw new UnsupportedOperationException("Unable to access specified method " + methodName, e); 211 } catch (IllegalArgumentException e) { 212 throw new UnsupportedOperationException("Illegal arguments supplied for method " + methodName, 213 e); 214 } catch (InvocationTargetException e) { 215 throw new UnsupportedOperationException("Method threw an exception for " + methodName, e); 216 } 217 } 218 219 private static Class<?>[] getParameterTypes(Object[] params) { 220 Class<?>[] parameterTypes = new Class<?>[params.length]; 221 for (int i = 0; i < params.length; i++) { 222 parameterTypes[i] = params[i].getClass(); 223 } 224 return parameterTypes; 225 } 226 227}