top of page

Java, Spring Boot နှင့် OSHI ဖြင့် မိမိကွန်ပျူတာအတွက် System Dashboard တစ်ခု တည်ဆောက်ခြင်း

  • Writer: Sunfyre
    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


Spring Initializr interface showing project setup options. Includes Maven, Java version 17, Spring Boot 3.5.5, and dependencies like Spring Web.

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


  1. Project ကို run ပါ

  2. Browser ဖြင့် စမ်းကြည့်ပါ

  3. Access http://localhost:8080.


Example UI


PC System Dashboard showing system overview, memory usage, and disk summary.

You should see your PC system data in UI.

 
 
 

Comments


Single post: Blog_Single_Post_Widget
bottom of page