View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.util;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.PrintStream;
23  import java.io.UnsupportedEncodingException;
24  import java.lang.management.ManagementFactory;
25  import java.lang.management.ThreadInfo;
26  import java.lang.management.ThreadMXBean;
27  import java.lang.reflect.Constructor;
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.nio.charset.Charset;
31  
32  import org.apache.commons.logging.Log;
33  
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
35  
36  @InterfaceAudience.Private
37  public class ReflectionUtils {
38    @SuppressWarnings("unchecked")
39    public static <T> T instantiateWithCustomCtor(String className,
40        Class<? >[] ctorArgTypes, Object[] ctorArgs) {
41      try {
42        Class<? extends T> resultType = (Class<? extends T>) Class.forName(className);
43        Constructor<? extends T> ctor = resultType.getDeclaredConstructor(ctorArgTypes);
44        return instantiate(className, ctor, ctorArgs);
45      } catch (ClassNotFoundException e) {
46        throw new UnsupportedOperationException(
47            "Unable to find " + className, e);
48      } catch (NoSuchMethodException e) {
49        throw new UnsupportedOperationException(
50            "Unable to find suitable constructor for class " + className, e);
51      }
52    }
53  
54    private static <T> T instantiate(final String className, Constructor<T> ctor, Object[] ctorArgs) {
55      try {
56        return ctor.newInstance(ctorArgs);
57      } catch (IllegalAccessException e) {
58        throw new UnsupportedOperationException(
59            "Unable to access specified class " + className, e);
60      } catch (InstantiationException e) {
61        throw new UnsupportedOperationException(
62            "Unable to instantiate specified class " + className, e);
63      } catch (InvocationTargetException e) {
64        throw new UnsupportedOperationException(
65            "Constructor threw an exception for " + className, e);
66      }
67    }
68  
69    @SuppressWarnings("unchecked")
70    public static <T> T newInstance(Class<T> type, Object... params) {
71      return instantiate(type.getName(), findConstructor(type, params), params);
72    }
73  
74    @SuppressWarnings("unchecked")
75    public static <T> Constructor<T> findConstructor(Class<T> type, Object... paramTypes) {
76      Constructor<T>[] constructors = (Constructor<T>[])type.getConstructors();
77      for (Constructor<T> ctor : constructors) {
78        Class<?>[] ctorParamTypes = ctor.getParameterTypes();
79        if (ctorParamTypes.length != paramTypes.length) {
80          continue;
81        }
82  
83        boolean match = true;
84        for (int i = 0; i < ctorParamTypes.length && match; ++i) {
85          Class<?> paramType = paramTypes[i].getClass();
86          match = (!ctorParamTypes[i].isPrimitive()) ? ctorParamTypes[i].isAssignableFrom(paramType) :
87                    ((int.class.equals(ctorParamTypes[i]) && Integer.class.equals(paramType)) ||
88                     (long.class.equals(ctorParamTypes[i]) && Long.class.equals(paramType)) ||
89                     (double.class.equals(ctorParamTypes[i]) && Double.class.equals(paramType)) ||
90                     (char.class.equals(ctorParamTypes[i]) && Character.class.equals(paramType)) ||
91                     (short.class.equals(ctorParamTypes[i]) && Short.class.equals(paramType)) ||
92                     (boolean.class.equals(ctorParamTypes[i]) && Boolean.class.equals(paramType)) ||
93                     (byte.class.equals(ctorParamTypes[i]) && Byte.class.equals(paramType)));
94        }
95  
96        if (match) {
97          return ctor;
98        }
99      }
100     throw new UnsupportedOperationException(
101       "Unable to find suitable constructor for class " + type.getName());
102   }
103 
104   /* synchronized on ReflectionUtils.class */
105   private static long previousLogTime = 0;
106   private static final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
107 
108   /**
109    * Log the current thread stacks at INFO level.
110    * @param log the logger that logs the stack trace
111    * @param title a descriptive title for the call stacks
112    * @param minInterval the minimum time from the last
113    */
114   public static void logThreadInfo(Log log,
115                                    String title,
116                                    long minInterval) {
117     boolean dumpStack = false;
118     if (log.isInfoEnabled()) {
119       synchronized (ReflectionUtils.class) {
120         long now = System.currentTimeMillis();
121         if (now - previousLogTime >= minInterval * 1000) {
122           previousLogTime = now;
123           dumpStack = true;
124         }
125       }
126       if (dumpStack) {
127         try {
128           ByteArrayOutputStream buffer = new ByteArrayOutputStream();
129           printThreadInfo(new PrintStream(buffer, false, "UTF-8"), title);
130           log.info(buffer.toString(Charset.defaultCharset().name()));
131         } catch (UnsupportedEncodingException ignored) {
132           log.warn("Could not write thread info about '" + title +
133               "' due to a string encoding issue.");
134         }
135       }
136     }
137   }
138 
139   /**
140    * Print all of the thread's information and stack traces.
141    *
142    * @param stream the stream to
143    * @param title a string title for the stack trace
144    */
145   private static void printThreadInfo(PrintStream stream,
146                                      String title) {
147     final int STACK_DEPTH = 20;
148     boolean contention = threadBean.isThreadContentionMonitoringEnabled();
149     long[] threadIds = threadBean.getAllThreadIds();
150     stream.println("Process Thread Dump: " + title);
151     stream.println(threadIds.length + " active threads");
152     for (long tid: threadIds) {
153       ThreadInfo info = threadBean.getThreadInfo(tid, STACK_DEPTH);
154       if (info == null) {
155         stream.println("  Inactive");
156         continue;
157       }
158       stream.println("Thread " +
159                      getTaskName(info.getThreadId(),
160                                  info.getThreadName()) + ":");
161       Thread.State state = info.getThreadState();
162       stream.println("  State: " + state);
163       stream.println("  Blocked count: " + info.getBlockedCount());
164       stream.println("  Waited count: " + info.getWaitedCount());
165       if (contention) {
166         stream.println("  Blocked time: " + info.getBlockedTime());
167         stream.println("  Waited time: " + info.getWaitedTime());
168       }
169       if (state == Thread.State.WAITING) {
170         stream.println("  Waiting on " + info.getLockName());
171       } else  if (state == Thread.State.BLOCKED) {
172         stream.println("  Blocked on " + info.getLockName());
173         stream.println("  Blocked by " +
174                        getTaskName(info.getLockOwnerId(),
175                                    info.getLockOwnerName()));
176       }
177       stream.println("  Stack:");
178       for (StackTraceElement frame: info.getStackTrace()) {
179         stream.println("    " + frame.toString());
180       }
181     }
182     stream.flush();
183   }
184 
185   private static String getTaskName(long id, String name) {
186     if (name == null) {
187       return Long.toString(id);
188     }
189     return id + " (" + name + ")";
190   }
191 
192   /**
193    * Get and invoke the target method from the given object with given parameters
194    * @param obj the object to get and invoke method from
195    * @param methodName the name of the method to invoke
196    * @param params the parameters for the method to invoke
197    * @return the return value of the method invocation
198    */
199   public static Object invokeMethod(Object obj, String methodName, Object... params) {
200     Method m;
201     try {
202       m = obj.getClass().getMethod(methodName, getParameterTypes(params));
203       m.setAccessible(true);
204       return m.invoke(obj, params);
205     } catch (NoSuchMethodException e) {
206       throw new UnsupportedOperationException("Cannot find specified method " + methodName, e);
207     } catch (IllegalAccessException e) {
208       throw new UnsupportedOperationException("Unable to access specified method " + methodName, e);
209     } catch (IllegalArgumentException e) {
210       throw new UnsupportedOperationException("Illegal arguments supplied for method " + methodName,
211         e);
212     } catch (InvocationTargetException e) {
213       throw new UnsupportedOperationException("Method threw an exception for " + methodName, e);
214     }
215   }
216 
217   private static Class<?>[] getParameterTypes(Object[] params) {
218     Class<?>[] parameterTypes = new Class<?>[params.length];
219     for (int i = 0; i < params.length; i++) {
220       parameterTypes[i] = params[i].getClass();
221     }
222     return parameterTypes;
223   }
224 
225 }