001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 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      final Class<?> serverConnectionClass = Class.forName("javax.management.MBeanServerConnection");
122      GET_ATTRIBUTE_METHOD = serverConnectionClass.getDeclaredMethod("getAttribute", OBJECT_NAME_CLASS, String.class);
123      final 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      final Class<?> notifListenerClass = Class.forName("javax.management.NotificationListener");
129      final 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      final Class<?> jmxClass = Class.forName("javax.management.JMX");
133      NEW_PROXY_METHOD = jmxClass.getDeclaredMethod("newMBeanProxy", serverConnectionClass, OBJECT_NAME_CLASS, Class.class, boolean.class);
134      final Class<?> mbeanInfoClass = Class.forName("javax.management.MBeanInfo");
135      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      final Class<?> factoryClass = Class.forName("java.lang.management.ManagementFactory");
140      Method m = factoryClass.getDeclaredMethod("getThreadMXBean");
141      THREAD_MXBEAN = m.invoke(null);
142      final 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 (final 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    final 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  @SuppressWarnings("unchecked")
228  public static <T> T newProxy(final Object connection, final Object mbeanName, final Class<T> inf) throws Exception {
229    if (!isManagementAvailable()) return null;
230    return (T) NEW_PROXY_METHOD.invoke(null, connection, mbeanName, inf, true);
231  }
232
233  /**
234   * Add the specified notification listener.
235   * @param connection the connection through which to add the listener.
236   * @param mbeanName the name of the mbean on which to a the listener.
237   * @param listener the notification listener to add.
238   * @param filter the notification filter.
239   * @param handback the handback object.
240   * @throws Exception if any error occurs.
241   */
242  public static void addNotificationListener(final Object connection, final String mbeanName, final Object listener, final Object filter, final Object handback) throws Exception {
243    if (!isManagementAvailable()) return;
244    ADD_NOTIFICATION_LISTENER_METHOD.invoke(connection, mbeanName, getObjectName(mbeanName), listener, filter, handback);
245  }
246
247  /**
248   * Remove the specified notification listener.
249   * @param connection the connection through which to add the listener.
250   * @param mbeanName the name of the mbean on which to a the listener.
251   * @param listener the notification listener to add.
252   * @param filter the notification filter.
253   * @param handback the handback object.
254   * @throws Exception if any error occurs.
255   */
256  public static void removeNotificationListener(final Object connection, final String mbeanName, final Object listener, final Object filter, final Object handback) throws Exception {
257    if (!isManagementAvailable()) return;
258    REMOVE_NOTIFICATION_LISTENER_METHOD.invoke(connection, mbeanName, getObjectName(mbeanName), listener, filter, handback);
259  }
260
261  /**
262   * Get the information for notifications supported by the specified MBean.
263   * @param connection the connection through which to get the mbean info.
264   * @param mbeanName the name of the mbean on which to a the mbean info.
265   * @return an array of {@code MBeanNotificationInfo} instances.
266   * @throws Exception if any error occurs.
267   */
268  public static Object getMBeanNotificationsInfo(final Object connection, final String mbeanName) throws Exception {
269    if (!isManagementAvailable()) return null;
270    final Object mbeanInfo = GET_MBEAN_INFO_METHOD.invoke(connection, getObjectName(mbeanName));
271    return GET_MBEAN_NOTIFICATIONS_INFO_METHOD.invoke(mbeanInfo);
272  }
273
274  /**
275   * Determine whether management is available for the current JVM.
276   * @return {@code true} if management is available, {@code false} otherwise.
277   */
278  public static boolean isManagementAvailable() {
279    return managementAvailable;
280  }
281
282  /**
283   * Get the platform MBean server.
284   * @return a {@code MBeanServerObject}.
285   */
286  public static Object getPlatformServer() {
287    return PLATFORM_SERVER;
288  }
289
290  /**
291   * Determine whether the MBean with the specified is already registered.
292   * @param mbeanName the name of the MBean to check.
293   * @return {@code true} if the MBean is registered, {@code false} otherwise.
294   * @throws Exception if any error occurs.
295   */
296  public static boolean isMBeanRegistered(final String mbeanName) throws Exception {
297    if (!isManagementAvailable()) return false;
298    return (Boolean) IS_MBEAN_REGISTERED_METHOD.invoke(PLATFORM_SERVER, getObjectName(mbeanName));
299  }
300
301  /**
302   * Get or create and cache the object name for the specified name.
303   * @param name the name for which to create a {@code javax.management.ObjectName}.
304   * @return an {@code javax.management.ObjectName} as an object.
305   */
306  private static Object getObjectName(final String name) {
307    Object o = objectNames.get(name);
308    if (o == null) {
309      try {
310        o = OBJECT_NAME_CONSTRUCTOR.newInstance(name);
311        objectNames.put(name, o);
312      } catch (final Exception e) {
313        if (debugEnabled) log.debug("could not create ObjectName for " + name, e);
314      }
315    }
316    return o;
317  }
318
319  /**
320   * Whether CPU time measurement is enabled/supported.
321   * @return {@code true} if time measurement is enabled, {@code false} otherwise.
322   */
323  public static boolean isCpuTimeEnabled() {
324    return CPU_TIME_ENABLED;
325  }
326
327  /**
328   * Determien the CPU time for the specified thread.
329   * @param threadID the id of the thread.
330   * @return the CPU time in nanoseconds, or -1L if the cpu time is not supported or could not be measured.
331   */
332  public static long getThreadCpuTime(final long threadID) {
333    if (!isManagementAvailable()) return -1L;
334    try {
335      return (Long) GET_THREAD_CPU_TIME_METHOD.invoke(THREAD_MXBEAN, threadID);
336    } catch(@SuppressWarnings("unused") final Exception e) {
337      return -1L;
338    }
339  }
340
341  /**
342   * Determien the user time for the specified thread.
343   * @param threadID the id of the thread.
344   * @return the user time in nanoseconds, or -1L if the user time is not supported or could not be measured.
345   */
346  public static long getThreadUserTime(final long threadID) {
347    if (!isManagementAvailable()) return -1L;
348    try {
349      return (Long) GET_THREAD_USER_TIME_METHOD.invoke(THREAD_MXBEAN, threadID);
350    } catch(@SuppressWarnings("unused") final Exception e) {
351      return -1L;
352    }
353  }
354
355  /**
356   *
357   * @param args not use.
358   * @throws Throwable if any error occurs.
359   */
360  public static void main(final String[] args) throws Throwable {
361  }
362}