001/*
002 * JPPF.
003 * Copyright (C) 2005-2016 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * 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 */
018
019package org.jppf.utils;
020
021import java.lang.reflect.*;
022import java.util.concurrent.ConcurrentHashMap;
023
024import org.jppf.utils.configuration.JPPFProperties;
025import org.slf4j.*;
026
027/**
028 *
029 * @author Laurent Cohen
030 */
031public class ManagementUtils {
032  /**
033   * Logger for this class.
034   */
035  private static Logger log = LoggerFactory.getLogger(ManagementUtils.class);
036  /**
037   * Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
038   */
039  private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
040  /**
041   * Determines whether the debug level is enabled in the logging configuration, without the cost of a method call.
042   */
043  private static boolean managementAvailable = true;
044  /**
045   * The the thread MXBean iself.
046   */
047  private static Object THREAD_MXBEAN = null;
048  /**
049   * Whether getting the cpu time is supported.
050   */
051  private static boolean CPU_TIME_ENABLED = false;
052  /**
053   * The method that gets the thread cpu time from the {@code ThreadMXBean}.
054   */
055  private static Method GET_THREAD_CPU_TIME_METHOD = null;
056  /**
057   * The method that gets the user time from the {@code ThreadMXBean}.
058   */
059  private static Method GET_THREAD_USER_TIME_METHOD = null;
060  /**
061   * The name of the operating system MXBean.
062   */
063  private static Object PLATFORM_SERVER = null;
064  /**
065   * The name of the operating system MXBean.
066   */
067  private static Method GET_ATTRIBUTE_METHOD = null;
068  /**
069   * The name of the operating system MXBean.
070   */
071  private static Method SET_ATTRIBUTE_METHOD = null;
072  /**
073   * Constructor for {@code javax.management.Attribute.Attribute(String, Object)}.
074   */
075  private static Constructor<?> ATTRIBUTE_CONSTRUCTOR = null;
076  /**
077   * The name of the operating system MXBean.
078   */
079  private static Method INVOKE_METHOD = null;
080  /**
081   * Method {@code javax.management.MBeanServerConnection.isRegistered(ObjectName)}.
082   */
083  private static Method IS_MBEAN_REGISTERED_METHOD = null;
084  /**
085   * Method {@code javax.management.JMX.newMBeanProxy(MBeanServerConnection, ObjectName, CLass<?>, boolean)}.
086   */
087  private static Method NEW_PROXY_METHOD = null;
088  /**
089   * Method {@code javax.management.MBeanServerConnection.addNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object)}.
090   */
091  private static Method ADD_NOTIFICATION_LISTENER_METHOD = null;
092  /**
093   * Method {@code javax.management.MBeanServerConnection.removeNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object)}.
094   */
095  private static Method REMOVE_NOTIFICATION_LISTENER_METHOD = null;
096  /**
097   * Method {@code javax.management.MBeanServerConnection.getMBeanInfo(ObjectName)}.
098   */
099  private static Method GET_MBEAN_INFO_METHOD = null;
100  /**
101   * Method {@code javax.management.MBeanInfo.getNotifications()}.
102   */
103  private static Method GET_MBEAN_NOTIFICATIONS_INFO_METHOD = null;
104  /**
105   * The class object {@code for javax.management.ObjectName}.
106   */
107  private static Class<?> OBJECT_NAME_CLASS;
108  /**
109   * Constructor for {@code javax.management.ObjectName.ObjectName(String)}.
110   */
111  private static Constructor<?> OBJECT_NAME_CONSTRUCTOR = null;
112  /**
113   *
114   */
115  private static final ConcurrentHashMap<String, Object> objectNames = new ConcurrentHashMap<>();
116
117  static {
118    try {
119      OBJECT_NAME_CLASS = Class.forName("javax.management.ObjectName");
120      OBJECT_NAME_CONSTRUCTOR = OBJECT_NAME_CLASS.getConstructor(String.class);
121      Class<?> serverConnectionClass = Class.forName("javax.management.MBeanServerConnection");
122      GET_ATTRIBUTE_METHOD = serverConnectionClass.getDeclaredMethod("getAttribute", OBJECT_NAME_CLASS, String.class);
123      Class<?> attributeClass = Class.forName("javax.management.Attribute");
124      ATTRIBUTE_CONSTRUCTOR = attributeClass.getConstructor(String.class, Object.class);
125      SET_ATTRIBUTE_METHOD = serverConnectionClass.getDeclaredMethod("setAttribute", OBJECT_NAME_CLASS, attributeClass);
126      INVOKE_METHOD = serverConnectionClass.getDeclaredMethod("invoke", OBJECT_NAME_CLASS, String.class, Object[].class, String[].class);
127      IS_MBEAN_REGISTERED_METHOD = serverConnectionClass.getDeclaredMethod("isRegistered", OBJECT_NAME_CLASS);
128      Class<?> notifListenerClass = Class.forName("javax.management.NotificationListener");
129      Class<?> notifFilterClass = Class.forName("javax.management.NotificationFilter");
130      ADD_NOTIFICATION_LISTENER_METHOD = serverConnectionClass.getDeclaredMethod("addNotificationListener", OBJECT_NAME_CLASS, notifListenerClass, notifFilterClass, Object.class);
131      REMOVE_NOTIFICATION_LISTENER_METHOD = serverConnectionClass.getDeclaredMethod("removeNotificationListener", OBJECT_NAME_CLASS, notifListenerClass, notifFilterClass, Object.class);
132      Class<?> jmxClass = Class.forName("javax.management.JMX");
133      NEW_PROXY_METHOD = jmxClass.getDeclaredMethod("newMBeanProxy", serverConnectionClass, OBJECT_NAME_CLASS, Class.class, boolean.class);
134      Class<?> mbeanInfoClass = Class.forName("javax.management.MBeanInfo");
135      Class<?> mbeanNotificationInfoClass = Class.forName("javax.management.MBeanNotificationInfo");
136      GET_MBEAN_INFO_METHOD = serverConnectionClass.getDeclaredMethod("getMBeanInfo", OBJECT_NAME_CLASS);
137      GET_MBEAN_NOTIFICATIONS_INFO_METHOD = mbeanInfoClass.getDeclaredMethod("getNotifications");
138
139      Class<?> factoryClass = Class.forName("java.lang.management.ManagementFactory");
140      Method m = factoryClass.getDeclaredMethod("getThreadMXBean");
141      THREAD_MXBEAN = m.invoke(null);
142      Class<?> threadMXBeanClass = Class.forName("java.lang.management.ThreadMXBean");
143      m = threadMXBeanClass.getDeclaredMethod("isThreadCpuTimeSupported");
144      CPU_TIME_ENABLED = (Boolean) m.invoke(THREAD_MXBEAN);
145      if (CPU_TIME_ENABLED) {
146        m = threadMXBeanClass.getDeclaredMethod("setThreadCpuTimeEnabled", boolean.class);
147        m.invoke(THREAD_MXBEAN, true);
148        GET_THREAD_CPU_TIME_METHOD = threadMXBeanClass.getDeclaredMethod("getThreadCpuTime", long.class);
149        GET_THREAD_USER_TIME_METHOD = threadMXBeanClass.getDeclaredMethod("getThreadUserTime", long.class);
150      }
151
152      m = factoryClass.getDeclaredMethod("getPlatformMBeanServer");
153      PLATFORM_SERVER = m.invoke(null);
154      log.debug("management successfully initialized");
155    } catch (Exception e) {
156      managementAvailable = false;
157      if (!JPPFConfiguration.get(JPPFProperties.NODE_ANDROID)) log.error("management could not be initialized, exception: {}", ExceptionUtils.getStackTrace(e));
158      //e.printStackTrace();
159    }
160  }
161
162  /**
163   * Invoke the given operation on the given mbean via the specified mbean server connection.
164   * @param connection the mbean server connection to use.
165   * @param mbeanName the name of the mbean on which to invoke an operation.
166   * @param operationName the name of the operation to invoke.
167   * @param params the params of the operation invocation.
168   * @param signature the types of the parameters.
169   * @return the result of the invokcation, or {@code null} if management is not available.
170   * @throws Exception if any error occurs.
171   */
172  public static Object invoke(final Object connection, final String mbeanName, final String operationName, final Object[] params, final String[] signature) throws Exception {
173    if (!isManagementAvailable()) return null;
174    return INVOKE_METHOD.invoke(connection, getObjectName(mbeanName), operationName, params, signature);
175  }
176
177  /**
178   * Get the given attribute of the given mbean via the specified mbean server connection.
179   * @param connection the mbean server connection to use.
180   * @param mbeanName the name of the mbean on which to invoke an attribute.
181   * @param attributeName the name of the attribute to get.
182   * @return the value of the attrribute, or {@code null} if management is not available.
183   * @throws Exception if any error occurs.
184   */
185  public static Object getAttribute(final Object connection, final String mbeanName, final String attributeName) throws Exception {
186    if (!isManagementAvailable()) return null;
187    return GET_ATTRIBUTE_METHOD.invoke(connection, getObjectName(mbeanName), attributeName);
188  }
189
190  /**
191   * Get the given attribute of the given mbean via the specified mbean server connection.
192   * @param connection the mbean server connection to use.
193   * @param mbeanName the name of the mbean on which to invoke an attribute.
194   * @param attributeName the name of the attribute to get.
195   * @param value the value of the attrribute to set.
196   * @throws Exception if any error occurs.
197   */
198  public static void setAttribute(final Object connection, final String mbeanName, final String attributeName, final Object value) throws Exception {
199    if (!isManagementAvailable()) return;
200    Object attribute = ATTRIBUTE_CONSTRUCTOR.newInstance(attributeName, value);
201    SET_ATTRIBUTE_METHOD.invoke(connection, getObjectName(mbeanName), attribute);
202  }
203
204  /**
205   * Create a proxy for the specified mbean interface.
206   * @param <T> the type of the proxy to return.
207   * @param connection the connection through which to get the rpoxy.
208   * @param mbeanName the name of the mbean for which to get a proxy.
209   * @param inf the mbean interface for which to get a proxy.
210   * @return a proxy instance of the the psecified interface, or {@code null} if management is not available.
211   * @throws Exception if any error occurs.
212   */
213  public static <T> T newProxy(final Object connection, final String mbeanName, final Class<T> inf) throws Exception {
214    if (!isManagementAvailable()) return null;
215    return newProxy(connection, getObjectName(mbeanName), inf);
216  }
217
218  /**
219   * Create a proxy for the specified mbean interface.
220   * @param <T> the type of the proxy to return.
221   * @param connection the connection through which to get the proxy.
222   * @param mbeanName the name of the mbean for which to get a proxy should be an instance of {@code ObjectName}.
223   * @param inf the mbean interface for which to get a proxy.
224   * @return a proxy instance of the the psecified interface, or {@code null} if management is not available.
225   * @throws Exception if any error occurs.
226   */
227  public static <T> T newProxy(final Object connection, final Object mbeanName, final Class<T> inf) throws Exception {
228    if (!isManagementAvailable()) return null;
229    return (T) NEW_PROXY_METHOD.invoke(null, connection, mbeanName, inf, true);
230  }
231
232  /**
233   * Add the specified notification listener.
234   * @param connection the connection through which to add the listener.
235   * @param mbeanName the name of the mbean on which to a the listener.
236   * @param listener the notification listener to add.
237   * @param filter the notification filter.
238   * @param handback the handback object.
239   * @throws Exception if any error occurs.
240   */
241  public static void addNotificationListener(final Object connection, final String mbeanName, final Object listener, final Object filter, final Object handback) throws Exception {
242    if (!isManagementAvailable()) return;
243    ADD_NOTIFICATION_LISTENER_METHOD.invoke(connection, mbeanName, getObjectName(mbeanName), listener, filter, handback);
244  }
245
246  /**
247   * Remove the specified notification listener.
248   * @param connection the connection through which to add the listener.
249   * @param mbeanName the name of the mbean on which to a the listener.
250   * @param listener the notification listener to add.
251   * @param filter the notification filter.
252   * @param handback the handback object.
253   * @throws Exception if any error occurs.
254   */
255  public static void removeNotificationListener(final Object connection, final String mbeanName, final Object listener, final Object filter, final Object handback) throws Exception {
256    if (!isManagementAvailable()) return;
257    REMOVE_NOTIFICATION_LISTENER_METHOD.invoke(connection, mbeanName, getObjectName(mbeanName), listener, filter, handback);
258  }
259
260  /**
261   * Get the information for notifications supported by the specified MBean.
262   * @param connection the connection through which to get the mbean info.
263   * @param mbeanName the name of the mbean on which to a the mbean info.
264   * @return an array of {@code MBeanNotificationInfo} instances.
265   * @throws Exception if any error occurs.
266   */
267  public static Object getMBeanNotificationsInfo(final Object connection, final String mbeanName) throws Exception {
268    if (!isManagementAvailable()) return null;
269    Object mbeanInfo = GET_MBEAN_INFO_METHOD.invoke(connection, getObjectName(mbeanName));
270    return GET_MBEAN_NOTIFICATIONS_INFO_METHOD.invoke(mbeanInfo);
271  }
272
273  /**
274   * Determine whether management is available for the current JVM.
275   * @return {@code true} if management is available, {@code false} otherwise.
276   */
277  public static boolean isManagementAvailable() {
278    return managementAvailable;
279  }
280
281  /**
282   * Get the platform MBean server.
283   * @return a {@code MBeanServerObject}.
284   */
285  public static Object getPlatformServer() {
286    return PLATFORM_SERVER;
287  }
288
289  /**
290   * Determine whether the MBean with the specified is already registered.
291   * @param mbeanName the name of the MBean to check.
292   * @return {@code true} if the MBean is registered, {@code false} otherwise.
293   * @throws Exception if any error occurs.
294   */
295  public static boolean isMBeanRegistered(final String mbeanName) throws Exception {
296    if (!isManagementAvailable()) return false;
297    return (Boolean) IS_MBEAN_REGISTERED_METHOD.invoke(PLATFORM_SERVER, getObjectName(mbeanName));
298  }
299
300  /**
301   * Get or create and cache the object name for the specified name.
302   * @param name the name for which to create a {@code javax.management.ObjectName}.
303   * @return an {@code javax.management.ObjectName} as an object.
304   */
305  private static Object getObjectName(final String name) {
306    Object o = objectNames.get(name);
307    if (o == null) {
308      try {
309        o = OBJECT_NAME_CONSTRUCTOR.newInstance(name);
310        objectNames.put(name, o);
311      } catch (Exception e) {
312        if (debugEnabled) log.debug("could not create ObjectName for " + name, e);
313      }
314    }
315    return o;
316  }
317
318  /**
319   * Whether CPU time measurement is enabled/supported.
320   * @return {@code true} if time measurement is enabled, {@code false} otherwise.
321   */
322  public static boolean isCpuTimeEnabled() {
323    return CPU_TIME_ENABLED;
324  }
325
326  /**
327   * Determien the CPU time for the specified thread.
328   * @param threadID the id of the thread.
329   * @return the CPU time in nanoseconds, or -1L if the cpu time is not supported or could not be measured.
330   */
331  public static long getThreadCpuTime(final long threadID) {
332    if (!isManagementAvailable()) return -1L;
333    try {
334      return (Long) GET_THREAD_CPU_TIME_METHOD.invoke(THREAD_MXBEAN, threadID);
335    } catch(Exception e) {
336      return -1L;
337    }
338  }
339
340  /**
341   * Determien the user time for the specified thread.
342   * @param threadID the id of the thread.
343   * @return the user time in nanoseconds, or -1L if the user time is not supported or could not be measured.
344   */
345  public static long getThreadUserTime(final long threadID) {
346    if (!isManagementAvailable()) return -1L;
347    try {
348      return (Long) GET_THREAD_USER_TIME_METHOD.invoke(THREAD_MXBEAN, threadID);
349    } catch(Exception e) {
350      return -1L;
351    }
352  }
353
354  /**
355   *
356   * @param args not use.
357   * @throws Throwable if any error occurs.
358   */
359  public static void main(final String[] args) throws Throwable {
360  }
361}