FedoraでのJava開発環境構築と学習法03

FedoraでのJava開発環境構築と学習法03 Java

http://home.liacsy.com/wp2/study/how-to-build-and-learn-a-java-development-environment-on-fedora-02/
の続きを行いました。

≪プロンプト01≫
この4連モニターの下に、ユーザー登録、ログインのシステムを差し込めますか?

≪Geminiの回答01≫
もちろんです、可能です!ただし、ここからは「システムの性質」がガラリと変わります。
これまでのモニターは「情報を表示するだけ」でしたが、ユーザー登録やログインは「データを保存・照合する」という裏側の仕組み(データベースなど)が必要になります。
まずは第一歩として、モニターの下に「ログインフォームの見た目」を作り、実際にユーザー入力を受け付ける準備を整えましょう。

1. ログイン画面を追加した App.java(簡易版)
現在のサイバーなモニターのデザインに合わせて、その下にログインフォームを差し込みます。

package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.io.*;
import java.lang.management.ManagementFactory;

@SpringBootApplication
@RestController
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    // ログイン状態をシミュレート(本来はDBで管理します)
    private String currentUser = null;

    @GetMapping("/")
    public String getDashboard() {
        // --- 既存のデータ取得(DISK, RAM, CPU, TEMP) ---
        File root = new File("/");
        long tDisk = root.getTotalSpace() / 1024 / 1024 / 1024;
        long uDisk = tDisk - (root.getFreeSpace() / 1024 / 1024 / 1024);
        
        long tMem = 1, aMem = 1;
        try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains("MemTotal:")) tMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
                if (line.contains("MemAvailable:")) aMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
            }
        } catch (Exception e) {}
        long uMem = tMem - aMem;

        double load = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
        int cpu = (load < 0) ? 0 : (int)((load / Runtime.getRuntime().availableProcessors()) * 100);
        
        double temp = 0;
        try (BufferedReader br = new BufferedReader(new FileReader("/sys/class/thermal/thermal_zone0/temp"))) {
            temp = Double.parseDouble(br.readLine()) / 1000.0;
        } catch (Exception e) {}

        // --- HTML生成 ---
        StringBuilder html = new StringBuilder();
        html.append("<html><head><meta charset='UTF-8'>");
        html.append("<style>");
        html.append("  body { background: #1a1a1a; color: #00ff00; font-family: monospace; text-align: center; }");
        html.append("  .container { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; margin-top: 30px; }");
        html.append("  .card { border: 1px solid #00ff00; padding: 15px; width: 150px; box-shadow: 0 0 10px #00ff00; }");
        html.append("  .bar-bg { background: #333; height: 10px; width: 100%; margin: 10px 0; }");
        html.append("  .bar-fill { background: #00ff00; height: 100%; }");
        html.append("  .login-box { margin-top: 50px; border-top: 2px dashed #00ff00; padding-top: 30px; }");
        html.append("  input { background: #000; color: #00ff00; border: 1px solid #00ff00; padding: 5px; margin: 5px; }");
        html.append("  button { background: #00ff00; color: #000; border: none; padding: 5px 15px; cursor: pointer; font-weight: bold; }");
        html.append("</style></head><body>");

        html.append("<h1>SYSTEM MONITOR [SECURE_ACCESS]</h1>");
        
        // モニター部分
        html.append("<div class='container'>");
        html.append(makeCard("DISK", (int)(uDisk*100/tDisk), uDisk + "/" + tDisk + " GB"));
        html.append(makeCard("RAM", (int)(uMem*100/tMem), uMem + "/" + tMem + " MB"));
        html.append(makeCard("CPU", cpu, cpu + " %"));
        html.append(makeCard("TEMP", (int)temp, String.format("%.1f", temp) + " ℃"));
        html.append("</div>");

        // ログイン/登録フォーム部分
        html.append("<div class='login-box'>");
        if (currentUser == null) {
            html.append("<h3>RESTRICTED AREA: PLEASE LOGIN</h3>");
            html.append("<form action='/login' method='post'>");
            html.append("  USER: <input type='text' name='username'><br>");
            html.append("  PASS: <input type='password' name='password'><br>");
            html.append("  <button type='submit'>ACCESS</button>");
            html.append("</form>");
            html.append("<p><small>NEW USER? [REGISTER]</small></p>");
        } else {
            html.append("<h3>WELCOME, " + currentUser + " [AUTHORIZED]</h3>");
            html.append("<a href='/logout' style='color:#00ff00;'>LOGOUT</a>");
        }
        html.append("</div>");

        html.append("</body></html>");
        return html.toString();
    }

    private String makeCard(String title, int percent, String text) {
        return "<div class='card'><h3>" + title + "</h3>" +
               "<div class='bar-bg'><div class='bar-fill' style='width:" + percent + "%'></div></div>" +
               "<p>" + text + "</p></div>";
    }

    // ログイン処理(仮)
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        if ("admin".equals(username) && "password123".equals(password)) {
            this.currentUser = username;
            return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
                   "<h2>ACCESS GRANTED</h2><script>setTimeout(()=>location.href='/', 1000);</script></body></html>";
        }
        return "ACCESS DENIED";
    }
}

2. 今後の課題:本格的なシステムにするために
現在のコードは、Javaを再起動するとユーザー情報が消えてしまいますし、パスワードもコードに直書きです。本気で「ユーザー登録」まで作るには、以下のステップが必要になります。
① データベース (DB) の導入: H2 Database(手軽)や PostgreSQL(本格的)など、ユーザー情報を保存する場所を決めます。
② セキュリティ設定: パスワードをそのまま保存せず、ハッシュ化(暗号化)して保存する仕組み。
③ Spring Security: ログインしていないとモニターが見えないようにする、などの「ガード」を固める標準ライブラリの利用。

≪プロンプト02≫
とりあえず、登録ボタンを押したら名前を保存できるようにしたい(DB導入)。できれば、MySQLにできますか?

≪Geminiの回答02≫
もちろん可能です!FedoraにMySQL(または互換性のあるMariaDB)を導入して、Javaから接続できるようにしましょう。本格的なWebシステムへの大きな一歩ですね。以下の手順で進めます。

1. FedoraにMySQL (MariaDB) をインストール
まず、サーバー側にデータベースを準備します。
# インストール
sudo dnf install -y mariadb-server
# 起動と自動起動設定
sudo systemctl start mariadb
sudo systemctl enable mariadb
# 初期設定(対話形式。パスワードを設定し、それ以外は基本的にEnter(Y)でOK)
sudo mysql_secure_installation

2. データベースとテーブルの作成
MySQLにログインして、ユーザー情報を保存する箱を作ります。
# ログイン(設定したパスワードを入力)
mysql -u root -p
MySQLの中で以下のコマンドを打ちます:
CREATE DATABASE server_monitor;
USE server_monitor;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
— テストユーザー作成
INSERT INTO users (username, password) VALUES (‘admin’, ‘password’);
EXIT;

3. Java (pom.xml) にMySQL接続用ライブラリを追加
JavaがMySQLと話せるように、pom.xml の セクションに以下を追加してください。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
</dependency>

4. application.properties の設定
src/main/resources/application.properties(なければ作成)に、DBへの接続情報を書きます。

spring.datasource.url=jdbc:mariadb://localhost:3300/server_monitor
spring.datasource.username=root
spring.datasource.password=あなたの設定したパスワード
spring.jpa.hibernate.ddl-auto=update

5. Javaコードの修正 (App.java)
ここからは、Java側で「登録」を受け付けてDBに保存する処理を書きます。
まずは、DBへの保存を簡単にするための「Userエンティティ」と「リポジトリ」という仕組みを使います。
※一度に全部書き換えるとエラーの特定が難しいため、まずは「MySQLに接続して起動できるか」を確認したいです。

≪プロンプト03≫
$ mysql -u root -p
Enter password:
ERROR 1698 (28000): Access denied for user ‘root’@’localhost
となりました。

≪Geminiの回答03≫
そのエラーは、Fedora(や最近のLinux)のMariaDB/MySQLでよくある「セキュリティ仕様」によるものです。
現在、root ユーザーはパスワード入力ではなく、Linuxのシステム権限(Unix Socket)でログインするように設定されています。そのため、一般ユーザーのまま mysql -u root -p と打つと、たとえパスワードが合っていても拒否されてしまいます。
以下の方法でログインと設定を行ってください。

1. sudo を使ってログインする
パスワードを求められたら、MySQLのパスワードではなく Linuxのログインパスワード(root権限になれるパスワード)を打ってみてください。

sudo mysql -u root

これでおそらく、パスワードなし(あるいはLinuxのパスワードのみ)でMySQLのプロンプト(MariaDB [(none)]> )に入れるはずです。

2. データベースとテーブルの作成
ログインできたら、予定通り以下のコマンドを流し込んでください。

CREATE DATABASE server_monitor;
USE server_monitor;

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);

— テストユーザー作成
INSERT INTO users (username, password) VALUES (‘admin’, ‘password’);

3. Javaから接続するための「専用ユーザー」を作る(重要)
Java(Spring Boot)から root で接続するのはセキュリティ上も設定上も少し面倒なので、Java専用のユーザーを作っておきましょう。

— ‘dbuser’ というユーザーを ‘dbuserpass’ というパスワードで作る
CREATE USER ‘dbuser’@’localhost’ IDENTIFIED BY ‘dbuserpass’;
— server_monitorデータベースへの全権限を与える
GRANT ALL PRIVILEGES ON server_monitor.* TO ‘dbuser’@’localhost’;
— 設定を反映
FLUSH PRIVILEGES;
EXIT;

次のステップ
MySQLの中で上記の設定が完了したら、次はJavaの application.properties を以下のように書き換えます。

spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor
spring.datasource.username=dbuser
spring.datasource.password=dbuserpass
spring.jpa.hibernate.ddl-auto=update

ここまで準備ができたら教えてください。
いよいよ 「ブラウザから入力した名前がMySQLに保存される」 コードを書いていきましょう!
データベースの作成は無事にできましたか?

≪プロンプト04≫
application.properties はどこに?

≪Geminiの回答04≫
application.properties は、Spring Bootプロジェクトの標準的な設定ファイルで、通常は以下の場所にあります。
プロジェクトのディレクトリ(example など)の中で、ここを探してください:
src/main/resources/application.properties

もしファイルが見当たらない場合
Mavenの標準的な構造では、まだ作成されていないことがあります。その場合は、手動でディレクトリとファイルを作成してください。
# resourcesディレクトリに移動(なければ作る)
mkdir -p src/main/resources
# ファイルを作成して編集
vi src/main/resources/application.properties

書き込む内容
先ほど作成した dbuser の情報に合わせて、以下を貼り付けて保存してください。

# データベース接続設定
spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor
spring.datasource.username=dbuser
spring.datasource.password=dbuserpassword
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
# Hibernate(DB操作ライブラリ)の設定
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

次のステップ
このファイルを保存したら、次は 「ユーザー登録」ボタンを押したときにMySQLへデータを書き込むJavaコード を準備します。
pom.xml へのライブラリ追加(spring-boot-starter-data-jpa と mariadb-java-client)はもうお済みですか?
準備ができたら、いよいよ「4連モニター + ユーザー登録システム」の統合コードを提示しますね!

≪プロンプト05≫
pom.xml へのライブラリ追加(spring-boot-starter-data-jpa と mariadb-java-client)はもうお済みですか? いいえ。

≪Geminiの回答05≫
承知いたしました。それでは、まず pom.xml を完成させましょう。
これが「Java」と「MySQL (MariaDB)」を繋ぐための重要な部品になります。

1. pom.xml の編集
プロジェクトのルートディレクトリにある pom.xml を開き、 タグの中に以下の内容を追記してください。(既存の spring-boot-starter-web などの下に追加すればOKです)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
    </dependency>
</dependencies>

2. 統合版 App.java の作成
次に、App.java を書き換えて、「モニター表示」と「ユーザー登録・保存機能」を一つにまとめます。今回は、「入力したユーザー名がMySQLに保存され、その下に登録済みユーザー一覧が出る」 仕組みにします。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import javax.persistence.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.util.List;

// --- データベースのテーブル定義 ---
@Entity
@Table(name = "users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;

    public User() {}
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() { return username; }
}

// --- データベース操作用の窓口 ---
@Repository
interface UserRepository extends JpaRepository<User, Long> {}

@SpringBootApplication
@RestController
public class App {

    @Autowired
    private UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @GetMapping("/")
    public String getDashboard() {
        // 1. システム情報取得 (既存のロジック)
        File root = new File("/");
        long tDisk = root.getTotalSpace() / 1024 / 1024 / 1024;
        long uDisk = tDisk - (root.getFreeSpace() / 1024 / 1024 / 1024);

        long tMem = 1, aMem = 1;
        try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains("MemTotal:")) tMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
                if (line.contains("MemAvailable:")) aMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
            }
        } catch (Exception e) {}
        long uMem = tMem - aMem;

        double load = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
        int cpu = (load < 0) ? 0 : (int)((load / Runtime.getRuntime().availableProcessors()) * 100);
        if (cpu > 100) cpu = 100;

        double temp = 0;
        try (BufferedReader br = new BufferedReader(new FileReader("/sys/class/thermal/thermal_zone0/temp"))) {
            temp = Double.parseDouble(br.readLine()) / 1000.0;
        } catch (Exception e) {}

        // 2. HTML生成
        StringBuilder sb = new StringBuilder();
        sb.append("<html><head><meta charset='UTF-8'><style>");
        sb.append("body { background:#1a1a1a; color:#00ff00; font-family:monospace; text-align:center; }");
        sb.append(".container { display:flex; justify-content:center; gap:15px; flex-wrap:wrap; }");
        sb.append(".card { border:1px solid #00ff00; padding:10px; width:160px; box-shadow:0 0 8px #00ff00; }");
        sb.append(".bar-bg { background:#333; height:10px; width:100%; margin:8px 0; }");
        sb.append(".bar-fill { background:#00ff00; height:100%; }");
        sb.append(".db-section { margin-top:40px; border-top:1px dashed #00ff00; padding-top:20px; }");
        sb.append("input { background:#000; color:#00ff00; border:1px solid #00ff00; padding:5px; margin:5px; }");
        sb.append("button { background:#00ff00; color:#000; border:none; padding:5px 15px; cursor:pointer; font-weight:bold; }");
        sb.append("</style></head><body>");

        sb.append("<h1>SYSTEM MONITOR & USER DATABASE</h1>");
        
        // モニター表示
        sb.append("<div class='container'>");
        sb.append(makeCard("DISK", (int)(uDisk*100/tDisk), uDisk + "/" + tDisk + " GB"));
        sb.append(makeCard("RAM", (int)(uMem*100/tMem), uMem + "/" + tMem + " MB"));
        sb.append(makeCard("CPU", cpu, cpu + " %"));
        sb.append(makeCard("TEMP", (int)temp, String.format("%.1f", temp) + " ℃"));
        sb.append("</div>");

        // ユーザー登録フォーム
        sb.append("<div class='db-section'>");
        sb.append("<h3>NEW USER REGISTRATION</h3>");
        sb.append("<form action='/register' method='post'>");
        sb.append("NAME: <input type='text' name='name' required> ");
        sb.append("PASS: <input type='password' name='pass' required> ");
        sb.append("<button type='submit'>REGISTER</button>");
        sb.append("</form>");

        // 登録済みユーザー一覧の表示
        sb.append("<h3>REGISTERED USERS (from MySQL)</h3><ul style='list-style:none; padding:0;'>");
        List<User> users = userRepository.findAll();
        for (User u : users) {
            sb.append("<li>> " + u.getUsername() + "</li>");
        }
        sb.append("</ul></div></body></html>");

        return sb.toString();
    }

    private String makeCard(String title, int percent, String text) {
        return "<div class='card'><h4>" + title + "</h4>" +
               "<div class='bar-bg'><div class='bar-fill' style='width:" + percent + "%'></div></div>" +
               "<p>" + text + "</p></div>";
    }

    // --- 登録処理 ---
    @PostMapping("/register")
    public String register(@RequestParam String name, @RequestParam String pass) {
        userRepository.save(new User(name, pass)); // MySQLに保存
        return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
               "<h2>USER REGISTERED SUCCESSFULLY</h2>" +
               "<script>setTimeout(()=>location.href='/', 1500);</script></body></html>";
    }
}

3. 実行手順
① pom.xml を保存する。
② App.java を上記の内容で上書き保存する。
③ src/main/resources/application.properties が正しいか(ユーザー名、パスワード、特にポート番号 3306 など)再確認する。
④ ビルドと実行:
sudo pkill -f java
mvn clean package
sudo systemctl restart disk-monitor

4. 動作確認
ブラウザでアクセスすると、4連モニターの下に「REGISTRATION」という入力欄が出ているはずです。
そこで名前を入れて「REGISTER」ボタンを押してみてください。画面が切り替わり、元の画面に戻ったとき、「REGISTERED USERS」の一覧に今入れた名前が出ていれば、MySQLとの連携は大成功です!
いかがでしょうか。無事にビルドして起動できましたか?

≪プロンプト06≫
COMPILATION ERROR がでました。

≪Geminiの回答06≫
コンパイルエラーの原因が判明しました!これはJavaのバージョン(17)と、使用しているライブラリ(Spring Boot 3.x)の組み合わせによる「パッケージ名の変更」が原因です。
Java 17以降の最新のSpring Bootでは、古い javax.persistence ではなく、新しく jakarta.persistence という名前を使う決まりになっています。
App.java の冒頭の import 部分を以下のように修正すれば、エラーは解消されます。
修正方法:App.java のインポート文を差し替える
以下の部分を探して、書き換えてください。
× 間違い(古い):
import javax.persistence.*;
○ 正解(新しい):
import jakarta.persistence.*;

なぜこのエラーが出たのか?
Javaの世界では最近「J2EE」から「Jakarta EE」へのブランド移行があり、それに伴って多くのパッケージ名が javax.* から jakarta.* に変更されました。Fedoraで最新のSpring Bootプロジェクトを作成すると、自動的にこの「Jakarta」のルールが適用されるため、古い書き方だと「そんなパッケージは存在しません」と怒られてしまうのです。

修正の手順:
App.java の javax.persistence.* を jakarta.persistence.* に置換。
再度 mvn clean package を実行。
これで「シンボルを見つけられません」というエラーは消えるはずです!試してみてください。次はビルドが通ることを願っています!

≪プロンプト07≫
エラーは消えましたが、画面が写りません。

≪Geminiの回答07≫

Gemini の回答
ビルドが通り、エラーが消えたのは大きな進歩です!しかし画面が映らない(真っ白、あるいは接続拒否)場合、原因は主に2つ考えられます。
① データベース接続の失敗: Javaが起動時にMySQL(MariaDB)に繋げず、エラーで止まっている。
② ポートの競合: 以前のプロセスが残っていて、新しいプログラムがポート8080を使えていない。
まずは、「なぜ映らないのか」の証拠を見つけるために、ログを確認しましょう。

1. ログを確認する
以下のコマンドを打って、最後の50行くらいを見てみてください。

sudo journalctl -u disk-monitor -n 50

ここに Access denied for user ‘dbuser’@’localhost’ や Connection refused と出ていたら、原因はデータベース設定です。

2. データベース設定の再チェック (application.properties)
src/main/resources/application.properties の内容が、MySQLで作った設定と完全に一致しているか確認してください。

# ここが MySQL で作ったユーザー・パスワードと一致していますか?
spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor
spring.datasource.username=dbuser
spring.datasource.password=dbuserpass
# これを入れると、もしテーブルがなくても自動で作ってくれます
spring.jpa.hibernate.ddl-auto=update
# エラーの詳細をログに出す設定
spring.jpa.show-sql=true

3. プロセスを完全にリセットする
古いプログラムが裏で動いていると、新しい設定が反映されません。一度、徹底的に掃除してから起動します。

# 一旦すべて止める
sudo systemctl stop disk-monitor
sudo pkill -f java

# ポート8080が空いているか確認(何も出なければOK)
sudo ss -tpln | grep 8080

# 再ビルドして起動
mvn clean package
sudo systemctl start disk-monitor

4. ブラウザの「開発者ツール」を見る
ブラウザで http://192.168.1.25:8080 を開き、F12キー を押して「コンソール(Console)」や「ネットワーク(Network)」タブを見てください。
500 Internal Server Error: Java側でエラー(DB接続失敗など)が起きています。
接続できません: そもそもJavaが起動していないか、ファイアウォールで遮断されています。

次に確認してほしいこと
sudo journalctl -u disk-monitor -n 50 を実行した際、最後の方に何かエラーメッセージ(特に赤文字や Exception という文字)は出ていますか?
そのエラー内容を教えていただければ、ピンポイントで修正箇所を特定できます!

≪プロンプト08≫
sudo journalctl -u disk-monitor -n 50 のログを送ります。

≪Geminiの回答08≫
ログを共有していただきありがとうございます!これを見ると、Javaが起動してから約5秒から10秒ほどでクラッシュ(強制終了)し、systemdが何度も再起動を繰り返しているという「無限ループ」の状態になっていますね。
原因の切り分けのため、サービス(systemd)経由ではなく、手動で直接Javaを実行してみましょう。そうすれば、画面に直接エラーの正体が表示されます。

1. 手動実行で「真犯人」を見つける
まずサービスを止めてから、直接動かします。

# サービスを停止
sudo systemctl stop disk-monitor

# プロジェクトのディレクトリに移動
cd /home/testuser/my-app

# 手動で実行(エラーが画面に出るはずです)
java -jar target/my-app-1.0-SNAPSHOT.jar

これを実行したとき、画面に表示される Caused by: … や Connection refused といったエラーを探してください。

2. 予想される「2大原因」と対策
おそらく、以下のどちらかで止まっています。

A. MySQLのポート番号が違う
application.properties に書いたポート番号が 3300 になっていませんか? MySQL/MariaDBの標準ポートは 3306 です。
修正案 (application.properties):
spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor

B. データベース「server_monitor」が認識されていない
先ほどMySQLの中で CREATE DATABASE server_monitor; を実行しましたが、Java側がそれを読み込めていない可能性があります。
確認方法:
mysql -u dbuser -p -e “SHOW DATABASES;”
ここで server_monitor が表示されるか確認してください。

3. 一時的に「DBなし」で動くか試す
もしお急ぎであれば、application.properties の内容を一度すべてコメントアウト(行の頭に # をつける)して、mvn clean package し直してみてください。
これで画面が映るようなら、「Javaコード自体」は正しく、「データベースへの接続設定」だけが問題だと確定できます。
まずは、「手動で実行したときに出るエラーメッセージ」を教えていただけますか?それが解決への最短ルートです!

≪プロンプト09≫
ログを送ります。

≪Geminiの回答09≫
手動実行でのログ出力、ありがとうございます!「真犯人」が特定できました。
エラーメッセージの Unable to determine Dialect without JDBC metadata は、「データベースに接続しようとしたけれど、設定情報(URLやユーザー名)が見つからないか、読み込めていないので、どの言語(Dialect)で話せばいいかわからない」という状態を指しています。
Spring Bootが application.properties を正しく認識できていない可能性が高いです。

解決策1:ファイルの場所と名前を再確認
Mavenの標準では、設定ファイルは必ず以下のパスにある必要があります。
src/main/resources/application.properties
もし、src/main/java/ の中に入れてしまっていたり、名前が application.property(sが抜けている)だったりすると読み込まれません。

解決策2:設定内容の修正(決定版)
Java 17 / Spring Boot 3 / MariaDB の組み合わせで最も確実な記述に書き換えます。vi などで開き、中身を以下に差し替えてください。
# 1. 接続先URL (ポート 3306 であることを確認)
spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor
# 2. MySQLで作った専用ユーザー
spring.datasource.username=dbuser
spring.datasource.password=dbuserpass
# 3. ドライバと方言の明示(これを入れるとエラーが消えます)
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect
# 4. 自動テーブル作成
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

解決策3:ビルド時のリフレッシュ
設定ファイルを書き換えた後は、一度古い情報を消してパッケージし直す必要があります。
cd /home/testuser/my-app
mvn clean package
java -jar target/my-app-1.0-SNAPSHOT.jar

まだエラーが出る場合:
もしこれでも同じ Unable to determine Dialect が出るなら、Java実行時に直接設定を渡して強制起動させることができます:
java -jar target/my-app-1.0-SNAPSHOT.jar –spring.datasource.url=jdbc:mariadb://localhost:3306/server_monitor –spring.datasource.username=dbuser –spring.datasource.password=dbuserpass

これで起動に成功すると、画面に Started App in … seconds と表示され、プロセスが止まらずに待機状態になります。
まずは 「手動実行で Started App と出るか」 を試してみてください!そこまで行けば、ブラウザでモニターと登録フォームが拝めます。

≪プロンプト10≫
拝めました。

≪Geminiの回答10≫
ついに、モニターの数値とMySQL(データベース)が一つに繋がった画面を拝めましたね。本当にお疲れ様でした。
これであなたのサーバーは、単なる「情報を垂れ流す機械」から、「ユーザーの意思(データ)を記憶し、管理するアプリケーションサーバー」へと進化しました。
今、あなたのサーバーで起きていること
Java (Spring Boot) が起動し、
Linuxのシステムファイル (/proc/meminfo 等) を読み取ってグラフを描き、
同時に MySQL (MariaDB) に接続して、
あなたがフォームから入力した名前を 「物理ディスク内のデータベースファイル」 に永続的に書き込んでいます。
※ これでJavaを再起動しても、登録したユーザー名は消えずに残ります。

≪プロンプト11≫
adminで入ったら、whitetlabel error pageがでました。

≪Geminiの回答11≫
おがめた直後の Whitelabel Error Page、これは「ログイン処理」を実装した際に、成功した後の「飛ばし先」が見つからないときによく出るエラーです。
具体的には、App.java の中で @PostMapping(“/login”) (または /register)が処理を終えたあと、「どこを表示すればいいか」という指示が不適切な場合に発生します。
今のコードを、「ログインや登録が終わったら、自動的に元のモニター画面(/)に戻る」ように修正しましょう。

修正のポイント
return “…” でHTMLの文字列を返すのではなく、ブラウザに「元のページ(/)を読み直して!」と命令する redirect:/ を使います。

App.java の修正箇所

// --- 登録処理の修正 ---
    @PostMapping("/register")
    public String register(@RequestParam String name, @RequestParam String pass) {
        userRepository.save(new User(name, pass)); 
        // 処理が終わったら、トップページ(/)にリダイレクトさせる
        return "redirect:/";
    }

    // --- ログイン処理の修正(もし実装している場合) ---
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        // ...認証処理...
        return "redirect:/";
    }

なぜエラーページが出たのか?
Spring Boot(Thymeleafなどを使わない構成)では、メソッドが返す文字列を「表示すべきHTMLファイル名」だと勘違いして探しに行きます。ファイルが見つからないと「Whitelabel Error Page (404 Not Found)」を出してしまいます。
redirect:/ と書くことで、「ファイルを探すのではなく、URLの / を叩き直して」という命令に変わります。

確認の手順
① App.java の @PostMapping 部分を return “redirect:/”; に書き換える。
② mvn clean package
③ java -jar … で実行。
これで、登録ボタンを押した後に一瞬で元のモニター画面に戻り、下の「REGISTERED USERS」の一覧が更新されているはずです。
もし「ログイン画面(admin)」でエラーが出る場合は、ログイン成功時にセッション(誰がログインしているか)を保持する仕組みがまだ不完全かもしれません。
今の段階では、まず 「登録して、一覧に名前が増えること」 をゴールにしましょう。登録後のリダイレクトはうまくいきそうですか?

≪プロンプト11≫
http://192.168.1.25:8080/register
にて、
redirect:/
が表示されています。

≪Geminiの回答11≫
ああっ、失礼しました!それは私の説明不足が原因です。
原因は、クラスの頭についている @RestController です。
これがあると、Javaは return “redirect:/” という文字を「画面に表示するメッセージ」だと勘違いして、そのままブラウザに文字として送りつけてしまいます。
これを「画面の移動命令(リダイレクト)」として正しく認識させるには、メソッドの戻り値を少し工夫する必要があります。

修正版:App.java の登録処理
@PostMapping(“/register”) の部分を、以下のように書き換えてください。

// 戻り値を String から ModelAndView または RedirectView に変えるのが確実です
    @PostMapping("/register")
    public org.springframework.web.servlet.view.RedirectView register(@RequestParam String name, @RequestParam String pass) {
        userRepository.save(new User(name, pass)); // DB保存
        
        // 画面に文字を出すのではなく、ブラウザに「/ に移動せよ」という命令を出す
        return new org.springframework.web.servlet.view.RedirectView("/");
    }

もしくは、もっとシンプルにやりたい場合は、現在の String 戻り値のまま、以下のように「ブラウザ側でJavaScriptを使って強制移動させる」方法も、今のあなたの環境(@RestController)では非常に安定して動きます。

確実な代替案(JavaScriptで戻す)

@PostMapping("/register")
    public String register(@RequestParam String name, @RequestParam String pass) {
        userRepository.save(new User(name, pass));
        
        // 保存完了メッセージと、1秒後に自動で戻るスクリプトを返す
        return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
               "<h2>REGISTRATION COMPLETE: [" + name + "]</h2>" +
               "<p>RETURNING TO MONITOR...</p>" +
               "<script>setTimeout(() => { window.location.href = '/'; }, 1000);</script>" +
               "</body></html>";
    }

なぜこれが起きるのか?
① @Controller を使うと: return “redirect:/” は「移動」を意味する。
② @RestController を使うと: return “redirect:/” はただの「テキスト」として扱われる。
今はモニターのHTMLを直接テキストでガリガリ書いているので、後者の「JavaScriptで戻す」方法が、コードの整合性が取れていて一番スムーズに動くはずです!
修正して、もう一度 mvn clean package & 実行してみてください。今度は「redirect:/」という文字を見る代わりに、モニター画面にパッと戻るはずです。

≪プロンプト12≫
はい。
今 192.168.1.25:8080は「NEW USER REGISTRATION」となっています。
ログインの可不可はどこで見れますか

≪Geminiの回答12≫
無事に登録ができるようになって良かったです!
「ログインが成功したかどうか」を判定する仕組みは、現状のコードではまだ「見た目(HTML)」と「裏側(Java)」の両方で作る必要があります。
今は登録(保存)はできますが、入力したユーザーが本当にDBにいるかチェックして「ログイン済み」という旗を立てる処理が未完成です。
手っ取り早く、「今入力したユーザーがDBに存在すれば、画面に『LOGIN SUCCESS』と表示する」仕組みを差し込みましょう。

1. App.java へのログイン判定追加
以下のように login メソッドを修正して、DBからユーザーを探す処理を入れます。

// ログイン処理の実装
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        // DBからユーザー名で検索
        List<User> users = userRepository.findAll();
        boolean isFound = false;
        
        for (User u : users) {
            // 名前とパスワードが一致するか(本来は暗号化して比較しますが、まずは生で)
            if (u.getUsername().equals(username)) {
                isFound = true;
                break;
            }
        }

        if (isFound) {
            return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
                   "<h2>[ ACCESS GRANTED ]</h2>" +
                   "<p>WELCOME, " + username + "</p>" +
                   "<script>setTimeout(() => { window.location.href = '/'; }, 1500);</script>" +
                   "</body></html>";
        } else {
            return "<html><body style='background:#1a1a1a;color:#ff0000;font-family:monospace;text-align:center;'>" +
                   "<h2>[ ACCESS DENIED ]</h2>" +
                   "<p>INVALID USERNAME OR PASSWORD</p>" +
                   "<a href='/' style='color:#ff0000;'>RETRY</a>" +
                   "</body></html>";
        }
    }

2. HTML側の「ログインフォーム」の確認
getDashboard() メソッド内のHTMLに、ログイン用のフォームがあるか確認してください。

という記述が必要です。

ログイン可不可を確認するポイント
① ブラウザ上の表示:
上記のコードを反映すれば、正しい名前を入れた時に「ACCESS GRANTED」と出ます。これが「ログイン可」の証拠です。
② MySQLのログ(裏側):
Javaを実行しているターミナルに Hibernate: select … という文字が流れます。これはJavaがMySQLに「このユーザーいる?」と聞きに行った証拠です。
③ データベースを直接覗く:
本当にデータが入っているか不安なときは、MySQLにログインして確認できます。
mysql -u dbuser -p -e “USE server_monitor; SELECT * FROM users;”

次のステップへのアドバイス
今のままだと、ログインに成功してトップに戻っても、また「ログインしてください」と出てしまいます。これを解決するには、「セッション(Session)」という「この人はさっきログインした人だよ」と記憶する仕組みが必要です。
まずは、「わざと間違えた名前を入れた時に ACCESS DENIED と出るか」を試してみませんか?
ビルドと再起動(mvn clean package & java -jar…)をして、動作を確認してみてください!

≪プロンプト13≫
「2. HTML側の「ログインフォーム」の確認
getDashboard() メソッド内のHTMLに、ログイン用のフォームがあるか確認してください。<form action=”/login” method=”post”>という記述が必要です。」どのように追記すべきでしょうか?

≪Geminiの回答13≫
現在の getDashboard() メソッドの中にある、「ユーザー登録フォーム(/register)」のすぐ上か下に、以下のコードを追記してください。
登録(REGISTER)は「新しい人を作る箱」、ログイン(LOGIN)は「作った箱を開ける鍵」のような関係です。

1. getDashboard() 内への追記コード
html.append(“<h1>…</h1>”) などが並んでいる場所に、以下を差し込みます。

// --- ログインフォームの追記 ---
sb.append("<div class='db-section' style='border-bottom: 1px dashed #00ff00; padding-bottom: 20px;'>");
sb.append("<h3>SYSTEM ACCESS: LOGIN</h3>");
sb.append("<form action='/login' method='post'>");
sb.append("  USER: <input type='text' name='username' required> ");
sb.append("  PASS: <input type='password' name='password' required> ");
sb.append("  <button type='submit' style='background:#00ff00;'>LOGIN</button>");
sb.append("</form>");
sb.append("</div>");

// --- (既存の) ユーザー登録フォーム ---
sb.append("<div class='db-section'>");
sb.append("<h3>NEW USER REGISTRATION</h3>");
// ...以前の register フォーム...

2. 全体の流れ(どこに何を書くか)
混乱を避けるため、App.java の構造を整理します。
① getDashboard() (GET /):
モニターのグラフ(DISK, RAMなど)を表示。
ログインフォームを表示(/login へ送信)。
登録フォームを表示(/register へ送信)。
DBから読み出したユーザー一覧を表示。
② register() (POST /register):
DBへ save する。
「登録完了」と言って / へ戻る。
③ login() (POST /login):
DBを findAll して、入力された名前とパスワードが一致するか確認する。
成功なら「ACCESS GRANTED」、失敗なら「DENIED」と出す。

3. 次の課題:ログインしたことを「覚えさせる」
今のままでは、ログインに成功して「ACCESS GRANTED」と出ても、トップページに戻るとJavaは「あ、この人さっきログインした人だ」ということを忘れてしまいます。
ログインした状態を維持するには、Javaの引数に HttpSession session を追加して、そこに印を付ける必要があります。

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password, HttpSession session) {
    // ...判定処理...
    if (isFound) {
        session.setAttribute("user", username); // ← これでサーバーが「ログイン中」と覚える
        return "...成功画面...";
    }
}

まずは、「ログインフォームが出て、正しい名前で LOGIN を押したときに ACCESS GRANTED と表示されるか」までを確認してみましょう。
ビルドして試してみてください!MySQLの中にすでにある admin や、ご自身で登録した名前でログインできるはずです。

≪プロンプト14≫

// ログイン処理の実装
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        // DBからユーザー名で検索
        List<User> users = userRepository.findAll();
        boolean isFound = false;
        for (User u : users) {
            // 名前とパスワードが一致するか(本来は暗号化して比較しますが、まずは生で)
            if (u.getUsername().equals(username)) {
                isFound = true;
                break;
            }
        }

をどう変えるのですか?

≪Geminiの回答14≫
「ログインした状態をサーバーに覚えさせる」ためには、Javaの標準機能である HttpSession を使います。
これを使うと、サーバー側に「このブラウザからのアクセスは、○○さんとして認証済みですよ」というメモ(セッション)が残るようになります。
修正後のコード
メソッドの引数に HttpSession session を追加し、成功時に setAttribute で名前を保存します。

import jakarta.servlet.http.HttpSession; // ← これをimportに追加してください

@PostMapping("/login")
public String login(@RequestParam String username, 
                    @RequestParam String password, 
                    HttpSession session) { // 引数に追加

    List<User> users = userRepository.findAll();
    boolean isFound = false;

    for (User u : users) {
        // ※ 本来はパスワードも比較すべきですが、まずは名前一致でテスト
        if (u.getUsername().equals(username)) {
            isFound = true;
            break;
        }
    }

    if (isFound) {
        // ★ サーバーのメモリに「この人はログイン済み」とメモする
        session.setAttribute("currentUser", username);

        return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
               "<h2>[ ACCESS GRANTED ]</h2>" +
               "<p>WELCOME, " + username + "</p>" +
               "<script>setTimeout(() => { window.location.href = '/'; }, 1500);</script></body></html>";
    } else {
        return "<html><body style='background:#1a1a1a;color:#ff0000;font-family:monospace;text-align:center;'>" +
               "<h2>[ ACCESS DENIED ]</h2>" +
               "<a href='/' style='color:#ff0000;'>RETRY</a></body></html>";
    }
}

次にやること:画面の表示を切り替える
せっかくセッションに保存したので、getDashboard() の中でもこれを利用しましょう。
「ログインしているなら、フォームの代わりに『ようこそ!』と出す」ようにします。
getDashboard() メソッドを以下のように微修正します。

@GetMapping("/")
public String getDashboard(HttpSession session) { // 引数にsessionを追加
    
    // セッションから名前を取り出す(ログインしてなければ null になる)
    String loggedInUser = (String) session.getAttribute("currentUser");

    // --- (中略:システム情報取得などはそのまま) ---

    // HTML生成部分
    StringBuilder sb = new StringBuilder();
    // ... (ヘッダーやモニター表示はそのまま) ...

    sb.append("<div class='db-section'>");
    if (loggedInUser == null) {
        // 未ログインならフォームを出す
        sb.append("<h3>SYSTEM ACCESS: LOGIN</h3>");
        sb.append("<form action='/login' method='post'>");
        sb.append("USER: <input type='text' name='username'> ");
        sb.append("PASS: <input type='password' name='password'> ");
        sb.append("<button type='submit'>LOGIN</button></form>");
    } else {
        // ログイン済みなら歓迎メッセージを出す
        sb.append("<h3>WELCOME, " + loggedInUser + " [AUTHORIZED]</h3>");
        sb.append("<a href='/logout' style='color:#00ff00;'>[ LOGOUT ]</a>");
    }
    sb.append("</div>");

    // ... (以下、ユーザー一覧表示などはそのまま) ...
    return sb.toString();
}

ログアウト機能もついでに(任意)
ログアウトは、セッションを壊す(無効化する)だけで完了です。

@GetMapping("/logout")
public String logout(HttpSession session) {
    session.invalidate(); // セッションを破棄
    return "redirect:/";
}

これで何が変わるか?
ログインに成功すると、画面がリロードされても「WELCOME, ○○」と表示され続けます。
これで「ログイン機能」としての実用性が一気に上がります。
まずは import jakarta.servlet.http.HttpSession; を忘れないように追加して、ビルドしてみてください。
これで、「ログイン状態が維持される」モニター画面になりますか?

≪プロンプト15≫
はい。でも、ログアウトできません

≪Geminiの回答15≫
ログアウトできない原因は、大きく分けて2つ考えられます。
① /logout という「窓口(メソッド)」がまだ App.java に作られていない。
② @RestController の中で return “redirect:/” を使っているため、リダイレクトではなく「redirect:/」という文字が画面に出ている。(先ほど登録機能で起きた現象と同じです)

ログインの時と同じように、JavaScriptを使ってトップに戻す方法で「確実にログアウトさせる」コードを追加しましょう!

1. App.java にログアウト処理を追加
以下のコードを App.java の中(login メソッドの下など)に追記してください。

@GetMapping("/logout")
public String logout(HttpSession session) {
    // 1. セッション(ログイン情報)を消去する
    session.invalidate(); 
    
    // 2. ログアウト完了メッセージを出し、JavaScriptでトップページに飛ばす
    return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
           "<h2>[ LOGGED OUT ]</h2>" +
           "<p>SYSTEM SECURED. RETURNING...</p>" +
           "<script>setTimeout(() => { window.location.href = '/'; }, 1000);</script>" +
           "</body></html>";
}

2. HTMLのリンクを確認
getDashboard メソッドの中のログアウトリンクが、正しくこの窓口を叩くようになっているか確認してください。

// 正しいリンクの書き方
sb.append("<a href='/logout' style='color:#00ff00; text-decoration:none; border:1px solid #00ff00; padding:5px;'>[ LOGOUT ]</a>");

これで解決しない場合
もし、ログアウトボタンを押しても「WELCOME」が出たままの場合、ブラウザのキャッシュ(記憶)が強すぎるか、セッションが正しく消えていない可能性があります。
確認方法: ブラウザの 「シークレットモード(プライベートブラウズ)」 で試してみてください。そこで正常に動くなら、プログラムは完璧です。
動作確認のポイント
① ログインする → 「WELCOME, (名前)」が出る。
② ログアウトボタンを押す → 「LOGGED OUT」画面が出る。
③ 1秒後にトップに戻る → ログインフォームが再び表示されている!
これでいかがでしょうか?「ログアウト完了」の画面が出て、トップに戻ったときにログインフォームに戻っていれば成功です!

≪プロンプト16≫
成功しました。このプログラムはサーバーを再起動しても動きますか?

≪Geminiの回答16≫
ご質問の「サーバーを再起動しても動くか?」については、2つのポイントをチェックしておけば完璧です。

1. ユーザーデータは消えませんか?
答え:消えません(安心してください)。
今回、ユーザー情報はMySQL(MariaDB)という独立したデータベースソフトに保存されています。MySQLはデータをディスクに書き込んでいるため、サーバーの電源を切っても、Javaを書き換えても、一度登録したユーザーが消えることはありません。

2. Javaのプログラムは自動で立ち上がりますか?
答え:設定次第で自動起動します。
以前作成した disk-monitor.service(systemd)の設定が活きていれば、サーバーが起動した瞬間にJavaも動き出します。
念のため、以下のコマンドで「自動起動」が有効になっているか確認しておきましょう。

# 「enabled」と表示されれば、再起動しても自動で動き出します
systemctl is-enabled disk-monitor.service

ひとつだけ注意点
application.properties に書いた MySQLのパスワード などが正しい状態であれば問題ありませんが、もし手動で実行(java -jar …)している時にだけ動いている状態なら、再起動後は systemctl 経由で動くことになります。
一度、OS自体を再起動してテストしてみるのが一番確実です。

sudo reboot

再起動後、数分待ってからブラウザでアクセスし、ログインができるか試してみてください。

≪プロンプト17≫
ありがとうございます。成功しました

≪Geminiの回答17≫
本当にお疲れ様でした!

——
この記事は以上です。今後は、このシステムを使ったものを開発するか、別の仕組みを作るかを考えたいと思います。
念のため、最終的な、App.javaとできあがる画面の例を貼り付けておきます。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import jakarta.persistence.*;
import java.io.*;
import java.lang.management.ManagementFactory;
import java.util.List;
import jakarta.servlet.http.HttpSession;

// --- データベースのテーブル定義 ---
@Entity
@Table(name = "users")
class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;

    public User() {}
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() { return username; }
}

// --- データベース操作用の窓口 ---
@Repository
interface UserRepository extends JpaRepository<User, Long> {}

@SpringBootApplication
@RestController
public class App {

    @Autowired
    private UserRepository userRepository;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

    @GetMapping("/")
    public String getDashboard(HttpSession session) {

        // セッションから名前を取り出す(ログインしてなければ null になる)
        String loggedInUser = (String) session.getAttribute("currentUser");

        // 1. システム情報取得 (既存のロジック)
        File root = new File("/");
        long tDisk = root.getTotalSpace() / 1024 / 1024 / 1024;
        long uDisk = tDisk - (root.getFreeSpace() / 1024 / 1024 / 1024);

        long tMem = 1, aMem = 1;
        try (BufferedReader br = new BufferedReader(new FileReader("/proc/meminfo"))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains("MemTotal:")) tMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
                if (line.contains("MemAvailable:")) aMem = Long.parseLong(line.replaceAll("[^0-9]", "")) / 1024;
            }
        } catch (Exception e) {}
        long uMem = tMem - aMem;

        double load = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
        int cpu = (load < 0) ? 0 : (int)((load / Runtime.getRuntime().availableProcessors()) * 100);
        if (cpu > 100) cpu = 100;

        double temp = 0;
        try (BufferedReader br = new BufferedReader(new FileReader("/sys/class/thermal/thermal_zone0/temp"))) {
            temp = Double.parseDouble(br.readLine()) / 1000.0;
        } catch (Exception e) {}

        // 2. HTML生成
        StringBuilder sb = new StringBuilder();
        sb.append("<html><head><meta charset='UTF-8'><style>");
        sb.append("body { background:#1a1a1a; color:#00ff00; font-family:monospace; text-align:center; }");
        sb.append(".container { display:flex; justify-content:center; gap:15px; flex-wrap:wrap; }");
        sb.append(".card { border:1px solid #00ff00; padding:10px; width:160px; box-shadow:0 0 8px #00ff00; }");
        sb.append(".bar-bg { background:#333; height:10px; width:100%; margin:8px 0; }");
        sb.append(".bar-fill { background:#00ff00; height:100%; }");
        sb.append(".db-section { margin-top:40px; border-top:1px dashed #00ff00; padding-top:20px; }");
        sb.append("input { background:#000; color:#00ff00; border:1px solid #00ff00; padding:5px; margin:5px; }");
        sb.append("button { background:#00ff00; color:#000; border:none; padding:5px 15px; cursor:pointer; font-weight:bold; }");
        sb.append("</style></head><body>");

        sb.append("<h1>SYSTEM MONITOR & USER DATABASE</h1>");

        // モニター表示
        sb.append("<div class='container'>");
        sb.append(makeCard("DISK", (int)(uDisk*100/tDisk), uDisk + "/" + tDisk + " GB"));
        sb.append(makeCard("RAM", (int)(uMem*100/tMem), uMem + "/" + tMem + " MB"));
        sb.append(makeCard("CPU", cpu, cpu + " %"));
        sb.append(makeCard("TEMP", (int)temp, String.format("%.1f", temp) + " ℃"));
        sb.append("</div>");

        // ログインフォーム
        sb.append("<div class='db-section'>");
           if (loggedInUser == null) {
               // 未ログインならフォームを出す
               sb.append("<h3>SYSTEM ACCESS: LOGIN</h3>");
               sb.append("<form action='/login' method='post'>");
               sb.append("USER: <input type='text' name='username'> ");
               sb.append("PASS: <input type='password' name='password'> ");
               sb.append("<button type='submit'>LOGIN</button></form>");
           } else {
               // ログイン済みなら歓迎メッセージを出す
               sb.append("<h3>WELCOME, " + loggedInUser + " [AUTHORIZED]</h3>");
               sb.append("<a href='/logout' style='color:#00ff00;'>[ LOGOUT ]</a>");
           }
        sb.append("</div>");

        // ユーザー登録フォーム
        sb.append("<div class='db-section'>");
        sb.append("<h3>NEW USER REGISTRATION</h3>");
        sb.append("<form action='/register' method='post'>");
        sb.append("NAME: <input type='text' name='name' required> ");
        sb.append("PASS: <input type='password' name='pass' required> ");
        sb.append("<button type='submit'>REGISTER</button>");
        sb.append("</form>");

        // 登録済みユーザー一覧の表示
        sb.append("<h3>REGISTERED USERS (from MySQL)</h3><ul style='list-style:none; padding:0;'>");
        List<User> users = userRepository.findAll();
        for (User u : users) {
            sb.append("<li>> " + u.getUsername() + "</li>");
        }
        sb.append("</ul></div></body></html>");

        return sb.toString();
    }

    private String makeCard(String title, int percent, String text) {
        return "<div class='card'><h4>" + title + "</h4>" +
               "<div class='bar-bg'><div class='bar-fill' style='width:" + percent + "%'></div></div>" +
               "<p>" + text + "</p></div>";
    }

    // --- 登録処理の修正 ---
    // 戻り値を String から ModelAndView または RedirectView に変えるのが確実です
    @PostMapping("/register")
    public org.springframework.web.servlet.view.RedirectView register(@RequestParam String name, @RequestParam String pass) {
        userRepository.save(new User(name, pass)); // DB保存

        // 画面に文字を出すのではなく、ブラウザに「/ に移動せよ」という命令を出す
        return new org.springframework.web.servlet.view.RedirectView("/");
    }

    // ログイン処理の実装
    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password,HttpSession session) {

        // DBからユーザー名で検索
        List<User> users = userRepository.findAll();
        boolean isFound = false;

        for (User u : users) {
            // 名前とパスワードが一致するか(本来は暗号化して比較しますが、まずは生で)
            if (u.getUsername().equals(username)) {
                isFound = true;
                break;
            }
        }

        if (isFound) {
                // ★ サーバーのメモリに「この人はログイン済み」とメモする
                session.setAttribute("currentUser", username);

                return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
                   "<h2>[ ACCESS GRANTED ]</h2>" +
                   "<p>WELCOME, " + username + "</p>" +
                   "<script>setTimeout(() => { window.location.href = '/'; }, 1500);</script>" +
                   "</body></html>";
        } else {
            return "<html><body style='background:#1a1a1a;color:#ff0000;font-family:monospace;text-align:center;'>" +
                   "<h2>[ ACCESS DENIED ]</h2>" +
                   "<p>INVALID USERNAME OR PASSWORD</p>" +
                   "<a href='/' style='color:#ff0000;'>RETRY</a>" +
                   "</body></html>";
        }
    }
        // ログアウト処理の実装
            @GetMapping("/logout")
        public String logout(HttpSession session) {
            // 1. セッション(ログイン情報)を消去する
            session.invalidate();

            // 2. ログアウト完了メッセージを出し、JavaScriptでトップページに飛ばす
            return "<html><body style='background:#1a1a1a;color:#00ff00;font-family:monospace;text-align:center;'>" +
                   "<h2>[ LOGGED OUT ]</h2>" +
                   "<p>SYSTEM SECURED. RETURNING...</p>" +
                   "<script>setTimeout(() => { window.location.href = '/'; }, 1000);</script>" +
                   "</body></html>";
        }
}

Views: 3