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}