001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  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.apache.hadoop.hbase.util;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.lang.management.ManagementFactory;
026import java.lang.management.OperatingSystemMXBean;
027import java.lang.management.RuntimeMXBean;
028import java.lang.reflect.Method;
029import java.nio.charset.StandardCharsets;
030
031import org.apache.yetus.audience.InterfaceAudience;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035
036/**
037 * This class is a wrapper for the implementation of
038 * com.sun.management.UnixOperatingSystemMXBean
039 * It will decide to use the sun api or its own implementation
040 * depending on the runtime (vendor) used.
041 */
042
043@InterfaceAudience.Private
044public class JVM {
045  private static final Logger LOG = LoggerFactory.getLogger(JVM.class);
046  private OperatingSystemMXBean osMbean;
047
048  private static final boolean ibmvendor =
049      System.getProperty("java.vendor") != null &&
050          System.getProperty("java.vendor").contains("IBM");
051  private static final boolean windows =
052      System.getProperty("os.name") != null &&
053          System.getProperty("os.name").startsWith("Windows");
054  private static final boolean linux =
055      System.getProperty("os.name") != null &&
056          System.getProperty("os.name").startsWith("Linux");
057  private static final boolean amd64 =
058      System.getProperty("os.arch") != null &&
059          System.getProperty("os.arch").contains("amd64");
060
061  private static final String JVMVersion = System.getProperty("java.version");
062
063  /**
064   * Constructor. Get the running Operating System instance
065   */
066  public JVM() {
067    this.osMbean = ManagementFactory.getOperatingSystemMXBean();
068  }
069
070  /**
071   * Check if the OS is unix.
072   *
073   * @return whether this is unix or not.
074   */
075  public static boolean isUnix() {
076    if (windows) {
077      return false;
078    }
079    return (ibmvendor ? linux : true);
080  }
081
082  /**
083   * Check if the OS is linux.
084   *
085   * @return whether this is linux or not.
086   */
087  public static boolean isLinux() {
088    return linux;
089  }
090
091  /**
092   * Check if the arch is amd64;
093   *
094   * @return whether this is amd64 or not.
095   */
096  public static boolean isAmd64() {
097    return amd64;
098  }
099
100  /**
101   * Check if the finish() method of GZIPOutputStream is broken
102   *
103   * @return whether GZIPOutputStream.finish() is broken.
104   */
105  public static boolean isGZIPOutputStreamFinishBroken() {
106    return ibmvendor && JVMVersion.contains("1.6.0");
107  }
108
109  /**
110   * Load the implementation of UnixOperatingSystemMXBean for Oracle jvm
111   * and runs the desired method.
112   *
113   * @param mBeanMethodName : method to run from the interface UnixOperatingSystemMXBean
114   *
115   * @return the method result
116   */
117  private Long runUnixMXBeanMethod(String mBeanMethodName) {
118    Object unixos;
119    Class<?> classRef;
120    Method mBeanMethod;
121
122    try {
123      classRef = Class.forName("com.sun.management.UnixOperatingSystemMXBean");
124      if (classRef.isInstance(osMbean)) {
125        mBeanMethod = classRef.getMethod(mBeanMethodName);
126        unixos = classRef.cast(osMbean);
127        return (Long) mBeanMethod.invoke(unixos);
128      }
129    } catch (Exception e) {
130      LOG.warn("Not able to load class or method for" +
131          " com.sun.management.UnixOperatingSystemMXBean.", e);
132    }
133    return null;
134  }
135
136  /**
137   * Get the number of opened filed descriptor for the runtime jvm.
138   * If Oracle java, it will use the com.sun.management interfaces.
139   * Otherwise, this methods implements it (linux only).
140   *
141   * @return number of open file descriptors for the jvm
142   */
143  public long getOpenFileDescriptorCount() {
144    Long ofdc;
145
146    if (!ibmvendor) {
147      ofdc = runUnixMXBeanMethod("getOpenFileDescriptorCount");
148      return (ofdc != null ? ofdc : -1);
149    }
150    InputStream inputStream = null;
151    InputStreamReader inputStreamReader = null;
152    BufferedReader bufferedReader = null;
153    try {
154      //need to get the PID number of the process first
155      RuntimeMXBean rtmbean = ManagementFactory.getRuntimeMXBean();
156      String rtname = rtmbean.getName();
157      String[] pidhost = rtname.split("@");
158
159      //using linux bash commands to retrieve info
160      Process p = Runtime.getRuntime().exec(
161          new String[]{"bash", "-c",
162              "ls /proc/" + pidhost[0] + "/fdinfo | wc -l"});
163      inputStream = p.getInputStream();
164      inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
165      bufferedReader = new BufferedReader(inputStreamReader);
166      String openFileDesCount;
167      if ((openFileDesCount = bufferedReader.readLine()) != null) {
168        return Long.parseLong(openFileDesCount);
169      }
170    } catch (IOException ie) {
171      LOG.warn("Not able to get the number of open file descriptors", ie);
172    } finally {
173      if (bufferedReader != null) {
174        try {
175          bufferedReader.close();
176        } catch (IOException e) {
177          LOG.warn("Not able to close the BufferedReader", e);
178        }
179      }
180      if (inputStreamReader != null) {
181        try {
182          inputStreamReader.close();
183        } catch (IOException e) {
184          LOG.warn("Not able to close the InputStreamReader", e);
185        }
186      }
187      if (inputStream != null) {
188        try {
189          inputStream.close();
190        } catch (IOException e) {
191          LOG.warn("Not able to close the InputStream", e);
192        }
193      }
194    }
195    return -1;
196  }
197
198  /**
199   * @see java.lang.management.OperatingSystemMXBean#getSystemLoadAverage
200   */
201  public double getSystemLoadAverage() {
202    return osMbean.getSystemLoadAverage();
203  }
204
205  /**
206   * @return the physical free memory (not the JVM one, as it's not very useful as it depends on
207   * the GC), but the one from the OS as it allows a little bit more to guess if the machine is
208   * overloaded or not).
209   */
210  public long getFreeMemory() {
211    if (ibmvendor) {
212      return 0;
213    }
214
215    Long r = runUnixMXBeanMethod("getFreePhysicalMemorySize");
216    return (r != null ? r : -1);
217  }
218
219
220  /**
221   * Workaround to get the current number of process running. Approach is the one described here:
222   * http://stackoverflow.com/questions/54686/how-to-get-a-list-of-current-open-windows-process-with-java
223   */
224  @edu.umd.cs.findbugs.annotations.SuppressWarnings(
225      value = "RV_DONT_JUST_NULL_CHECK_READLINE",
226      justification = "used by testing")
227  public int getNumberOfRunningProcess() {
228    if (!isUnix()) {
229      return 0;
230    }
231
232    InputStream inputStream = null;
233    InputStreamReader inputStreamReader = null;
234    BufferedReader bufferedReader = null;
235
236    try {
237      int count = 0;
238      Process p = Runtime.getRuntime().exec("ps -e");
239      inputStream = p.getInputStream();
240      inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
241      bufferedReader = new BufferedReader(inputStreamReader);
242      while (bufferedReader.readLine() != null) {
243        count++;
244      }
245      return count - 1; //  -1 because there is a headline
246    } catch (IOException e) {
247      return -1;
248    } finally {
249      if (bufferedReader != null) {
250        try {
251          bufferedReader.close();
252        } catch (IOException e) {
253          LOG.warn("Not able to close the BufferedReader", e);
254        }
255      }
256      if (inputStreamReader != null) {
257        try {
258          inputStreamReader.close();
259        } catch (IOException e) {
260          LOG.warn("Not able to close the InputStreamReader", e);
261        }
262      }
263      if (inputStream != null) {
264        try {
265          inputStream.close();
266        } catch (IOException e) {
267          LOG.warn("Not able to close the InputStream", e);
268        }
269      }
270    }
271  }
272
273  /**
274   * Get the number of the maximum file descriptors the system can use.
275   * If Oracle java, it will use the com.sun.management interfaces.
276   * Otherwise, this methods implements it (linux only).
277   *
278   * @return max number of file descriptors the operating system can use.
279   */
280  public long getMaxFileDescriptorCount() {
281    Long mfdc;
282    if (!ibmvendor) {
283      mfdc = runUnixMXBeanMethod("getMaxFileDescriptorCount");
284      return (mfdc != null ? mfdc : -1);
285    }
286    InputStream in = null;
287    BufferedReader output = null;
288    try {
289      //using linux bash commands to retrieve info
290      Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", "ulimit -n"});
291      in = p.getInputStream();
292      output = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
293      String maxFileDesCount;
294      if ((maxFileDesCount = output.readLine()) != null) {
295        return Long.parseLong(maxFileDesCount);
296      }
297    } catch (IOException ie) {
298      LOG.warn("Not able to get the max number of file descriptors", ie);
299    } finally {
300      if (output != null) {
301        try {
302          output.close();
303        } catch (IOException e) {
304          LOG.warn("Not able to close the reader", e);
305        }
306      }
307      if (in != null) {
308        try {
309          in.close();
310        } catch (IOException e) {
311          LOG.warn("Not able to close the InputStream", e);
312        }
313      }
314    }
315    return -1;
316  }
317}