View Javadoc
1   package com.guinetik.hexafun.examples.sysmon;
2   
3   import oshi.SystemInfo;
4   import oshi.hardware.CentralProcessor;
5   import oshi.hardware.GlobalMemory;
6   import oshi.software.os.OSFileStore;
7   
8   import java.util.List;
9   
10  /**
11   * Cross-platform metrics provider using OSHI library.
12   *
13   * <p>OSHI (Operating System and Hardware Information) provides native
14   * access to system metrics on Windows, Linux, macOS, and other platforms
15   * without JNI dependencies.</p>
16   *
17   * <p>This implementation includes WSL compatibility fixes.</p>
18   */
19  public class OshiMetricsProvider implements MetricsProvider {
20  
21      private final SystemInfo si = new SystemInfo();
22      private final CentralProcessor cpu = si.getHardware().getProcessor();
23      private final GlobalMemory memory = si.getHardware().getMemory();
24      private long[] prevTicks;
25  
26      public OshiMetricsProvider() {
27          // Prime the CPU tick counter - first reading needs baseline
28          prevTicks = cpu.getSystemCpuLoadTicks();
29          try {
30              Thread.sleep(100); // Brief pause to get meaningful delta
31          } catch (InterruptedException ignored) {}
32          prevTicks = cpu.getSystemCpuLoadTicks();
33      }
34  
35      @Override
36      public double getCpuUsage() {
37          double load = cpu.getSystemCpuLoadBetweenTicks(prevTicks) * 100;
38          prevTicks = cpu.getSystemCpuLoadTicks();
39          return Math.max(0, Math.min(100, load)); // Clamp to 0-100
40      }
41  
42      @Override
43      public double getMemoryUsage() {
44          long total = memory.getTotal();
45          long available = memory.getAvailable();
46          if (total <= 0) return 0;
47          return ((double) (total - available) / total) * 100.0;
48      }
49  
50      @Override
51      public double getDiskUsage() {
52          var fs = si.getOperatingSystem().getFileSystem();
53          List<OSFileStore> stores = fs.getFileStores();
54          if (stores.isEmpty()) return 0;
55  
56          // Find the best disk: prefer root "/" or "C:" over virtual mounts
57          OSFileStore best = stores.get(0);
58          long bestSize = 0;
59  
60          for (OSFileStore store : stores) {
61              String mount = store.getMount();
62              long total = store.getTotalSpace();
63  
64              // Skip virtual/empty filesystems
65              if (total <= 0) continue;
66  
67              // Prefer root mounts
68              if ("/".equals(mount) || mount.matches("[A-Z]:\\\\")) {
69                  best = store;
70                  break;
71              }
72  
73              // Otherwise pick the largest real filesystem
74              if (total > bestSize && !mount.startsWith("/snap") && !mount.startsWith("/boot")) {
75                  best = store;
76                  bestSize = total;
77              }
78          }
79  
80          long total = best.getTotalSpace();
81          long usable = best.getUsableSpace();
82          if (total <= 0) return 0;
83          return ((double) (total - usable) / total) * 100.0;
84      }
85  }