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.management.diagnostics;
020
021import java.io.*;
022import java.lang.management.*;
023
024import javax.management.*;
025
026import org.jppf.JPPFException;
027import org.jppf.utils.*;
028import org.slf4j.*;
029
030/**
031 * Implementation of the {@link DiagnosticsMBean} interface.
032 * @author Laurent Cohen
033 */
034public class Diagnostics implements DiagnosticsMBean, Closeable {
035  /**
036   * Logger for this class.
037   */
038  private static Logger log = LoggerFactory.getLogger(Diagnostics.class);
039  /**
040   * Determines whether the debug level is enabled in the log configuration, without the cost of a method call.
041   */
042  private static boolean debugEnabled = LoggingUtils.isDebugEnabled(log);
043  /**
044   * Reference to the platform's {@link ThreadMXBean} instance.
045   */
046  private final ThreadMXBean threadsMXBean = ManagementFactory.getThreadMXBean();
047  /**
048   * Collects regular snapshots of the total CPU time.
049   */
050  private CPUTimeCollector cpuTimeCollector = null;
051  /**
052   * Triggers a heap dump based on the JVM implementation.
053   */
054  private HeapDumpCollector heapDumpCollector = null;
055  /**
056   * Whether the full operating system MXBean features are available or not.
057   */
058  private boolean osMXBeanAvailable = true;
059  /**
060   * The object name of the operating system MXBean.
061   */
062  private ObjectName osMXBeanName = null;
063  /**
064   * The platform MBean server.
065   */
066  private static final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
067
068  /**
069   * Initialize this MBean implementation.
070   * @param closeableType the name of the type of closeable to use.
071   */
072  public Diagnostics(final String closeableType) {
073    init();
074    CloseableHandler.addResetCloseable(closeableType, this);
075  }
076
077  /**
078   * Initialize this Mbean.
079   */
080  private void init() {
081    if (debugEnabled) log.debug("initializing " + getClass().getSimpleName());
082    if (threadsMXBean.isThreadCpuTimeSupported()) {
083      if (debugEnabled) log.debug("Starting CPU time collector thread");
084      if (!threadsMXBean.isThreadCpuTimeEnabled()) threadsMXBean.setThreadCpuTimeEnabled(true);
085      try {
086        Class.forName("com.sun.management.OperatingSystemMXBean");
087        osMXBeanName = new ObjectName("java.lang", "type", "OperatingSystem");
088      } catch (Exception e) {
089        osMXBeanAvailable = false;
090        log.info("OperatingSystemMXBean not avaialble, an approximation of the process CPU load will be computed");
091      }
092      if (!osMXBeanAvailable) {
093        cpuTimeCollector = new CPUTimeCollector();
094        Thread thread = new Thread(cpuTimeCollector, "CPUTimeCollector");
095        thread.setDaemon(true);
096        thread.start();
097      }
098    } else if (debugEnabled) log.debug("CPU time collection is not supported - CPU load will be unavailable");
099    if (threadsMXBean.isThreadContentionMonitoringSupported()) {
100      if (!threadsMXBean.isThreadContentionMonitoringEnabled()) threadsMXBean.setThreadContentionMonitoringEnabled(true);
101    }
102    heapDumpCollector = HeapDumpCollector.Factory.newInstance();
103    if (heapDumpCollector == null) {
104      if (debugEnabled) log.debug("a heap dump collector could not be created for this JVM - no heap dumps will be available");
105    }
106  }
107
108  @Override
109  public MemoryInformation memoryInformation() throws Exception {
110    return new MemoryInformation();
111  }
112
113  @Override
114  public void gc() throws Exception {
115    System.gc();
116  }
117
118  @Override
119  public String[] threadNames() throws Exception {
120    long[] ids = threadsMXBean.getAllThreadIds();
121    ThreadInfo[] infos = threadsMXBean.getThreadInfo(ids, 0);
122    String[] result = new String[infos.length];
123    for (int i=0; i<infos.length; i++) result[i] = infos[i].getThreadName();
124    return result;
125  }
126
127  @Override
128  public ThreadDump threadDump() throws Exception {
129    checkThreadCapabilities();
130    return new ThreadDump(ManagementFactory.getThreadMXBean());
131  }
132
133  /**
134   * Ensure that thread contention monitoring is enabled, if it is supported.
135   */
136  private void checkThreadCapabilities() {
137    if (threadsMXBean.isThreadContentionMonitoringSupported() && !threadsMXBean.isThreadContentionMonitoringEnabled())
138      threadsMXBean.setThreadContentionMonitoringEnabled(true);
139  }
140
141  @Override
142  public Boolean hasDeadlock() throws Exception {
143    long[] ids = threadsMXBean.findDeadlockedThreads();
144    return (ids != null) && (ids.length > 0);
145  }
146
147  @Override
148  public HealthSnapshot healthSnapshot() throws Exception {
149    HealthSnapshot snapshot = new HealthSnapshot();
150    MemoryInformation memInfo = memoryInformation();
151    MemoryUsageInformation mem = memInfo.getHeapMemoryUsage();
152    snapshot.heapUsedRatio = mem.getUsedRatio();
153    snapshot.heapUsed = mem.getUsed();
154    mem = memInfo.getNonHeapMemoryUsage();
155    snapshot.nonheapUsedRatio = mem.getUsedRatio();
156    snapshot.nonheapUsed = mem.getUsed();
157    snapshot.deadlocked = hasDeadlock();
158    snapshot.liveThreads = threadsMXBean.getThreadCount();
159    snapshot.processCpuLoad = cpuLoad();
160    snapshot.systemCpuLoad = osMXBeanDoubleValue("SystemCpuLoad");
161    long freeRam = osMXBeanLongValue("FreePhysicalMemorySize");
162    if (freeRam >= 0L) {
163      long totalRam = osMXBeanLongValue("TotalPhysicalMemorySize");
164      snapshot.ramUsed = totalRam - freeRam;
165      snapshot.ramUsedRatio = (double) snapshot.ramUsed / (double) totalRam;
166    } else {
167      snapshot.ramUsed = -1L;
168      snapshot.ramUsedRatio = -1d;
169    }
170    return snapshot;
171  }
172
173  @Override
174  public String heapDump() throws Exception {
175    if (heapDumpCollector == null) throw new JPPFException("heap dumps are not available for this JVM");
176    return heapDumpCollector.dumpHeap();
177  }
178
179  @Override
180  public Double cpuLoad() {
181    if (osMXBeanAvailable) return osMXBeanDoubleValue("ProcessCpuLoad");
182    return cpuTimeCollector == null ? -1d : cpuTimeCollector.getLoad();
183  }
184
185  @Override
186  public void close() throws IOException {
187    if (cpuTimeCollector != null) cpuTimeCollector.setStopped(true);
188  }
189
190  /**
191   * Get the value of a double attribute from the OS mxbean.
192   * @param attribute the name of the attribute to get the value from.
193   * @return the attribute value as a double.
194   */
195  private double osMXBeanDoubleValue(final String attribute) {
196    if (osMXBeanAvailable) {
197      try {
198        return (Double) mbeanServer.getAttribute(osMXBeanName, attribute);
199      } catch (Exception e) {
200        if (debugEnabled) log.debug("error getting attribute '{}': {}", attribute, ExceptionUtils.getMessage(e));
201      }
202    }
203    return -1d;
204  }
205
206  /**
207   * Get the value of a double attribute from the OS mxbean.
208   * @param attribute the name of the attribute to get the value from.
209   * @return the attribute value as a double.
210   */
211  private long osMXBeanLongValue(final String attribute) {
212    if (osMXBeanAvailable) {
213      try {
214        return (long) mbeanServer.getAttribute(osMXBeanName, attribute);
215      } catch (Exception e) {
216        if (debugEnabled) log.debug("error getting attribute '{}': {}", attribute, ExceptionUtils.getMessage(e));
217      }
218    }
219    return -1L;
220  }
221}