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}