Java, Spring Boot နှင့် OSHI ဖြင့် မိမိကွန်ပျူတာအတွက် System Dashboard တစ်ခု တည်ဆောက်ခြင်း
- Sunfyre
- Aug 29
- 4 min read
မိမိကွန်ပျူတာ၏ system information များကို Java tools အသုံးပြုပြီး စောင့်ကြည့်နိုင်မည့် dashboard တစ်ခု ဖန်တီးခြင်း။
Introduction
System dashboard များသည် မိမိကွန်ပျူတာ၏ health, performance, နှင့် hardware specification များကို စောင့်ကြည့်နိုင်ရန် အလွန်အသုံးဝင်ပါသည်။ DevOps, backend developer, သို့မဟုတ် စက်တွေရဲ့အခြေအနေကို သိချင်တဲ့အခါ မိမိကိုယ်တိုင်ဖန်တီးတဲ့ dashboard တစ်ခုရှိတာက တန်ဖိုးရှိတဲ့အတွေ့အကြုံတစ်ခုဖြစ်ပါတယ်။
ဒီ blog မှာတော့ Java နှင့် Spring Boot အသုံးပြုပြီး system info dashboard တစ်ခုဖန်တီးခြင်းကို ဆွေးနွေးသွားမှာပါ။
အသုံးပြုမည့်နည်းပညာများမှာ -
Java 17+
Spring Boot
oshi-core (Open Source Hardware Info)
Thymeleaf
OSHI ဆိုတာဘာလဲ ?
OSHI (Operating System and Hardware Information) သည် Java library တစ်ခုဖြစ်ပြီး OS နှင့် hardware နှင့် ပတ်သက်သောအချက်အလက်များကို cross-platform ပေါ်တွင်သိရှိနိုင်အောင် API များဖြင့် ထောက်ပံ့ပေးသည်။ OSHI သည် native libraries မလိုအပ်ပါဘူး။ Java application တွေအတွက် system introspection ရယူရန်အတွက် အလွန်အဆင်ပြေပါတယ်။
OSHI ဖြင့်ရရှိနိုင်သောအချက်အလက်များမှာ -
CPU usage
RAM stats
Disk info
Motherboard details
OS metadata
Boot time and uptime
etc.
Let's start the project setup
1. Initialize Spring Boot Project
Spring Initializr (https://start.spring.io) သို့မဟုတ် IDE မှတဆင့် project တစ်ခုဖန်တီးပါ။ Dependency များအနေဖြင့်
Spring Web
Spring Boot DevTools
Thymeleaf

2. OSHI dependency ထည့်ပါ
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>6.8.3</version>
</dependency>
Creating the System Info Service
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
@Service
public class SystemInfoService {
private final SystemInfo systemInfo;
private final HardwareAbstractionLayer hal;
private final OperatingSystem os;
public SystemInfoService() {
this.systemInfo = new SystemInfo();
this.hal = systemInfo.getHardware();
this.os = systemInfo.getOperatingSystem();
}
public String getHostname() {
return os.getNetworkParams().getHostName();
}
public String getManufacturer() {
return hal.getComputerSystem().getManufacturer();
}
public String getModel() {
return hal.getComputerSystem().getModel();
}
public String getBootTime() {
long bootEpoch = os.getSystemBootTime();
LocalDateTime bootTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(bootEpoch), ZoneId.systemDefault());
return bootTime.toString();
}
public String getUptime() {
long seconds = os.getSystemUptime();
Duration duration = Duration.ofSeconds(seconds);
long days = duration.toDays();
long hours = duration.minusDays(days).toHours();
long minutes = duration.minusDays(days).minusHours(hours).toMinutes();
return String.format("%d days, %d hours, %d minutes", days, hours, minutes);
}
public String getCpuInfo() {
CentralProcessor processor = systemInfo.getHardware().getProcessor();
return processor.getProcessorIdentifier().getName();
}
public long getTotalMemory() {
GlobalMemory memory = systemInfo.getHardware().getMemory();
return memory.getTotal() / (1024 * 1024 * 1024); // in GB
}
public long getAvailableMemory() {
GlobalMemory memory = systemInfo.getHardware().getMemory();
return memory.getAvailable() / (1024 * 1024 * 1024); // in GB
}
public List<OSFileStore> getDiskInfo() {
return systemInfo.getOperatingSystem().getFileSystem().getFileStores();
}
public String getOsInfo() {
OperatingSystem os = systemInfo.getOperatingSystem();
return os.toString();
}
}
Create a Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.jonathan.assetmanagementsystem.service.SystemInfoService;
@Controller
public class DashboardController {
@Autowired
private SystemInfoService systemInfoService;
@GetMapping("/")
public String showDashboard(Model model) {
model.addAttribute("hostname", systemInfoService.getHostname());
model.addAttribute("os", systemInfoService.getOsInfo());
model.addAttribute("bootTime", systemInfoService.getBootTime());
model.addAttribute("uptime", systemInfoService.getUptime());
model.addAttribute("manufacturer", systemInfoService.getManufacturer());
model.addAttribute("model", systemInfoService.getModel());
model.addAttribute("cpu", systemInfoService.getCpuInfo());
model.addAttribute("totalMemory", systemInfoService.getTotalMemory());
model.addAttribute("availableMemory", systemInfoService.getAvailableMemory());
model.addAttribute("disks", systemInfoService.getDiskInfo());
return "dashboard_v2";
}
}
Create an UI with Thymleaf
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>System Dashboard</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<style>
body {
background-color: #2c3e50; /* Darker background */
color: #ecf0f1; /* Light text for contrast */
font-family: 'Poppins', sans-serif; /* Apply Google Font */
}
.container {
padding-top: 50px;
padding-bottom: 50px;
}
h1, h4, h5 {
color: #ecf0f1; /* Ensure headings are also light */
}
.card {
background-color: #34495e; /* Slightly lighter dark for cards */
border: none;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); /* Subtle shadow for depth */
border-radius: 10px; /* Slightly rounded corners */
}
.card-header {
background-color: #1abc9c !important; /* A vibrant accent color */
color: white !important;
border-bottom: none;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
font-weight: 600;
}
.card-title {
color: #ecf0f1; /* Card titles light */
font-weight: 500;
}
dt {
color: #bdc3c7; /* Muted color for labels */
font-weight: 400;
}
dd {
color: #ecf0f1; /* Data values light */
}
.table {
color: #ecf0f1; /* Table text light */
}
.table thead th {
border-bottom: 2px solid #1abc9c; /* Accent color for table header border */
color: #1abc9c; /* Accent color for table headers */
font-weight: 600;
}
.table tbody tr {
transition: background-color 0.3s ease; /* Smooth hover effect */
}
.table tbody tr:hover {
background-color: #4a627d; /* Lighter background on hover */
}
.table-bordered th, .table-bordered td {
border-color: #4a627d; /* Darker border for table cells */
}
.progress {
height: 25px; /* Make progress bars a bit taller */
background-color: #4a627d; /* Darker background for the empty part of progress bar */
}
.progress-bar {
background-color: #1abc9c; /* Vibrant color for filled part */
color: #ecf0f1;
font-weight: 500;
line-height: 25px; /* Vertically center text in progress bar */
}
.metric-value {
font-size: 1.25rem; /* Larger font for key metrics */
font-weight: 600;
color: #1abc9c; /* Accent color for key numbers */
}
</style>
</head>
<body>
<div class="container mt-4">
<h1 class="mb-4 text-center">PC System Dashboard</h1>
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-info-circle me-2"></i>
<h4 class="mb-0 d-inline-block">System Overview</h4>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-3"><i class="fas fa-desktop me-2"></i>Hostname</dt>
<dd class="col-sm-9" th:text="${hostname}"></dd>
<dt class="col-sm-3"><i class="fas fa-laptop-code me-2"></i>Operating System</dt>
<dd class="col-sm-9" th:text="${os}"></dd>
<dt class="col-sm-3"><i class="fas fa-building me-2"></i>Manufacturer</dt>
<dd class="col-sm-9" th:text="${manufacturer}"></dd>
<dt class="col-sm-3"><i class="fas fa-microchip me-2"></i>Model</dt>
<dd class="col-sm-9" th:text="${model}"></dd>
<dt class="col-sm-3"><i class="fas fa-microchip me-2"></i>CPU</dt>
<dd class="col-sm-9" th:text="${cpu}"></dd>
<dt class="col-sm-3"><i class="fas fa-clock me-2"></i>Boot Time</dt>
<dd class="col-sm-9" th:text="${bootTime}"></dd>
<dt class="col-sm-3"><i class="fas fa-hourglass-start me-2"></i>Uptime</dt>
<dd class="col-sm-9" th:text="${uptime}"></dd>
</dl>
</div>
</div>
<div class="row mb-3">
<div class="col-md-6">
<div class="card h-100">
<div class="card-body d-flex flex-column justify-content-between">
<div>
<h5 class="card-title"><i class="fas fa-memory me-2"></i>Memory Usage</h5>
<p class="card-text">
Total: <span class="metric-value" th:text="${totalMemory + ' GB'}"></span><br>
Available: <span class="metric-value" th:text="${availableMemory + ' GB'}"></span>
</p>
</div>
<div th:with="usedMemory=${totalMemory - availableMemory},
memoryPercentage=${T(java.lang.Math).round((usedMemory * 100.0) / totalMemory)}">
<div class="progress mt-3">
<div class="progress-bar" role="progressbar"
th:style="'width: ' + ${memoryPercentage} + '%'"
th:aria-valuenow="${memoryPercentage}"
aria-valuemin="0" aria-valuemax="100"
th:text="${memoryPercentage} + '% Used'">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100">
<div class="card-body d-flex flex-column justify-content-between">
<h5 class="card-title"><i class="fas fa-sd-card me-2"></i>Disk Usage Summary</h5>
<p class="card-text">
Total Disks: <span class="metric-value" th:text="${#lists.size(disks)}"></span>
</p>
<div class="alert alert-info bg-transparent border-0 p-0" style="color:#bdc3c7;">
<small>See detailed disk information below.</small>
</div>
</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-hdd me-2"></i>
<h4 class="mb-0 d-inline-block">Disks</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Mount</th>
<th>Type</th>
<th>Total (GB)</th>
<th>Usable (GB)</th>
<th>Used %</th> <!-- Added for percentage -->
</tr>
</thead>
<tbody>
<tr th:each="disk : ${disks}">
<td th:text="${disk.mount}"></td>
<td th:text="${disk.type}"></td>
<td th:with="totalGB=${disk.getTotalSpace() / 1073741824.0}"
th:text="${#numbers.formatDecimal(totalGB, 1, 2)}"></td>
<td th:with="usableGB=${disk.getUsableSpace() / 1073741824.0}"
th:text="${#numbers.formatDecimal(usableGB, 1, 2)}"></td>
<td th:with="totalBytes=${disk.getTotalSpace()},
usableBytes=${disk.getUsableSpace()},
usedBytes=${totalBytes - usableBytes},
diskPercentage=${T(java.lang.Math).round((usedBytes * 100.0) / totalBytes)}">
<div class="progress" style="min-width: 100px;">
<div class="progress-bar" role="progressbar"
th:style="'width: ' + ${diskPercentage} + '%'"
th:aria-valuenow="${diskPercentage}"
aria-valuemin="0" aria-valuemax="100"
th:text="${diskPercentage} + '%'">
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Run and Test
Project ကို run ပါ
Browser ဖြင့် စမ်းကြည့်ပါ
Access http://localhost:8080.
Example UI

You should see your PC system data in UI.
Comments