Ubuntu Proを使ってみる

少し前に個人利用であれば、Proバージョンを無償で使用できるようになった。そこで、このサイトとは別に個人で利用しているサーバーのUbuntuをProにアップグレードしてみる。

Ubuntu Oneアカウント作成

Proバージョンの利用を始めるには、Ubuntu Oneアカウント作成しTokenを発行する必要がある。まだ持っていない場合は、以下のURLにアクセスしてアカウントを作成する。
https://login.ubuntu.com/

ログイン後、トップ画面に戻り、右上メニューのUbuntu Pro dashboadをクリックする。

画面が切り替わり少し待つと以下のような画面が表示されFree Personal Tokenが発行される。

Ubuntu Proにアップグレードする

Ubuntu Proを利用したいサーバー上で以下のコマンドを入力する。

sudo apt update
sudo apt upgrade
# 必要ならここでサーバー再起動
sudo pro attach 先ほどのトークン
sudo apt install ubuntu-advantage-tools
sudo apt update
sudo apt upgrade

ChatGPTを使ってみる

ちょっと試してみたくなった。

ユーザー登録

ユーザー登録だけで、今は無料で使用できる。最初に以下のページにアクセスし、「TRY CHATGPT」をクリックしてユーザー登録を行う。
https://openai.com/blog/chatgpt/

使ってみる

試しに「ChatGPTを上手に使うには?」と質問してみた。

ChatGPT:ChatGPTを上手に使うには以下のようなポイントがあります。

  1. 明確な質問をすること: ChatGPTには具体的な質問をすることが重要です。
  2. 十分な情報を提供すること: 質問に対する回答を最適化するために、できるだけ多くの情報を提供することが大切です。
  3. 自然な言語を使うこと: ChatGPTは人工知能モデルですが、自然な言語を使うことで、より正確な回答を得ることができます。
  4. 信頼性の高い情報ソースからの情報を求めること: ChatGPTはトレーニングデータに基づいていますが、常に正確な情報を保証することはできません。正確な情報を求める場合は信頼性の高い情報ソースからの情報を求めることをお勧めします。

対話式に質問を続けられるはすなので、さらに意地悪に質問を掘り下げてみた。

「ChatGPTからの回答が間違えている可能性はどのくらいあるのか?」

ChatGPT:ChatGPTはOpenAIによってトレーニングされた大規模な言語モデルですが、常に正確な回答を保証することはできません。トレーニングデータに基づいて回答を生成しますが、トレーニングデータに誤りが含まれていたり、最新の情報が含まれていなかったりする場合があります。また、ChatGPTは意図や文脈を正確に理解することもできない場合があります。そのため、ChatGPTからの回答を単独で信頼することは避け、常に他の情報源と照らし合わせて判断することが重要です。

「そうではなくて、間違いが含まれている可能性の具体的な数値を知りたい。」

ChatGPT:具体的な数値は提供されていませんが、ChatGPTは高精度の言語モデルであり、多くの場合、正確な回答を生成します。ただし、トレーニングデータに基づいているため、トレーニングデータに含まれていない情報や最新の情報に対する回答など、間違いが含まれる可能性があります。また、文脈や意図の誤りなどもありますので、常に他の情報源と照らし合わせて判断することが重要です。

「最新の情報ほど間違いが含まれている可能性が高いということか?」

ChatGPT:はい、そのような傾向があります。トレーニングデータは一定期間前の情報であり、最新の情報は含まれていない可能性があります。また、最新の情報はまだ完全には明らかでない場合もありますので、正確性が低い可能性があります。そのため、最新の情報に関する質問に対しては特に他の情報源と照らし合わせて確認することが重要です。

まとめ

同じような回答になってしまっているところもあるが、自然に会話しているように文章が繋がっている。本当にすごいツールが出てきたと思う。

Java 8のOptionalクラスの考察

動機

Optionalについて、うまく使えていない気がするため、改めて整理してみる。

実装例

Nullチェック

nullの一番の問題点は、nullチェックを行わなくともコンパイルエラーにならないこと。そして実行時にNullPointerExceptionが発生する可能性があること。

例として、nullチェックを行う場合について考えてみる。

public String getFruit() {
    return 果物の名前または、nullを返却する処理とする;
}

public void fruitCheck() {
    var fruit = getFruit();

    if (fruit != null) {
        System.out.println(fruit + " are delicious.");            
    }
}

これを単純にOptionalに置き換えると下記のようになる。

public Optional<String> getFruit() {
    String name = 果物の名前または、nullを返却する処理とする;
    return Optional.ofNullable(name);
}

public void fruitCheck() {
    var fruit = getFruit();

    if (fruit.isPresent()) {
        System.out.println(fruit.get() + " are delicious.");
    }
}

これだと、nullチェックを行っているときと変わらない。この処理を典型的な関数型プログラミングのスタイルに書き換えてみる。

public void fruitCheck() {
    var fruit = getFruit();

    fruit.ifPresent(name -> System.out.println(name + " are delicious."));
}

elseの処理が必要な場合は、下記のように書ける。

fruit.ifPresentOrElse(name -> System.out.println(name + " are delicious."),
        () -> System.out.println("I'm hungry."));

Nullの場合にデフォルト値を設定する処理

getFruit()のメソッドでnullを返すのではなく、nullの場合デフォルトの値を返す場合を考えてみる。

public String getFruit() {
    String name = 果物の名前または、nullを返却する処理とする;

    if (name != null) {
        return name;
    } else {
        return "Apple";
    }
}

public void fruitCheck() {
    var fruit = getFruit();

    System.out.println(fruit + " are delicious.");            
}

Optionalに置き換えると下記のようになる。

public String getFruit() {
    String name = 果物の名前または、nullを返却する処理とする;

    return Optional.ofNullable(name).orElse("Apple");
}

関数型プログラミングのスタイルになって、Nullチェックのミスも減るだろうか。

Nullチェックするとともに値のチェックも行う場合

public void fruitCheck() {
    var fruit = getFruit();

    if (fruit != null && fruit.equals("Banana")) {
        System.out.println(fruit + " are very delicious.");            
    }
}
public void fruitCheck() {
    var fruit = getFruit();

    fruit.filter(name -> name.equals("Banana"))
            .ifPresent(name -> System.out.println(name + " are delicious."));
}

Nuxt 3が出たので使ってみる

動機

Vue 3に対応したNuxt 3がリリースされたので、早速使ってみる。

ゴール

以前作成した下記のアプリと同等の機能を実装する。すでに、Vue3対応は完了しているため、大きな変更は必要ないはず。
https://github.com/hide6644/crawler-client

今回はインストールまで行う。インストール途中に「

ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time

」のワーニングが表示されるが、今は気にしても仕方がないので、無視する。

インストール

実行環境
Windows 11
node 18

npx nuxi init プロジェクト名
cd プロジェクト名
npm install

実行

npm run dev

実行後、http://localhost:3000/にアクセスすると、welcomeページが表示される。

開発環境

開発環境には、Visual Studio Codeを使用する。インストール済みであれば、先ほどのインストールフォルダで、「code ./」を実行すると即座に起動できる。
(プラグインVolar ExtensionTypeScript Vue Plugin (Volar)のインストール推奨)

Javaでファイル内容を比較し、結果を別ファイルに出力する

目的

キーが1対1の関係になっている2つのファイルを1行ずつ読みだし内容を比較する。その比較結果を別のファイルに出力するプログラムをJavaで作成する。

要件

〇入力ファイル
 ・在庫ファイル
   商品コード
   在庫数
 ・注文ファイル
   商品コード
   注文数
 ※それぞれ、商品コードで一意、昇順になっている。

〇出力ファイル
 ・新在庫ファイル
   商品コード
   在庫数
 ・エラーファイル
   商品コード
   注文数
 新在庫ファイル・・・入力ファイルの在庫と注文を突合して、在庫数から注文数を引いた数量を記録する
 エラーファイル・・・注文に対して、在庫がない、在庫が足りない場合はエラーとし、当ファイルに出力する

〇その他
 入力ファイルのサイズが大きいため、1行ずつ読み込んで処理していく必要がある。

実装例

public void matching() {
    try (var stockFile = Files.newBufferedReader(Paths.get("src/stock.txt"));
            var orderFile = Files.newBufferedReader(Paths.get("src/order.txt"));
            var newStockFile = Files.newBufferedWriter(Paths.get("src/newStock.txt"));
            var errorOrderFile = Files.newBufferedWriter(Paths.get("src/errorOrder.txt"));) {
        // 入力ファイルの1行目を読み込む
        // 文字列からオブジェクトに変換する
        Stock stock = Stock.createFromLine(stockFile.readLine());
        Order order = Order.createFromLine(orderFile.readLine());

        // どちらかのファイルの行があるうちは処理を続ける
        while (stock.getKey() < Integer.MAX_VALUE || order.getKey() < Integer.MAX_VALUE) {
            // 比較するコードを取得。(この例ではコードを数値としている)
            // コードで昇順になっているのでコードの大小を比較すれば、
            // 一方のファイルにしかない商品もわかる
            int stockKey = stock.getKey();
            int orderKey = order.getKey();

            if (stockKey - orderKey == 0) {
                // コードが一致
                int qty = stock.getQuantity() - order.getQuantity();

                if (qty < 0) {
                    // 在庫不足
                    // エラーファイル出力
                    errorOrderFile.write(order.toErrorOrder("01").toLine());
                    errorOrderFile.newLine();
                } else {
                    stock.setQuantity(qty);
                }

                // オブジェクトから1行分の文字列を生成し、書き出す
                newStockFile.write(stock.toNewStock().toLine());
                newStockFile.newLine();
                // 次の行を読み込む
                stock = Stock.createFromLine(stockFile.readLine());
                order = Order.createFromLine(orderFile.readLine());
            } else if (stockKey - orderKey > 0) {
                // 商品なし
                // エラーファイル出力
                errorOrderFile.write(order.toErrorOrder("02").toLine());
                errorOrderFile.newLine();
                // 次の行を読み込む
                order = Order.createFromLine(orderFile.readLine());
            } else {
                // 注文なし
                newStockFile.write(stock.toNewStock().toLine());
                newStockFile.newLine();
                // 次の行を読み込む
                stock = Stock.createFromLine(stockFile.readLine());
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※他にも「var」宣言を使える箇所があるが、わかりやすくするため、あえて通常の宣言にしている。

@Builder
@Data
public class Order {

    private String category;
    private String code;
    private Integer quantity;

    public Integer getKey() {
        if (category == null && code == null) {
            return Integer.MAX_VALUE;
        } else {
            return Integer.valueOf(category + code);
        }
    }

    public ErrorOrder toErrorOrder(String errorCode) {
        return ErrorOrder.builder()
                .category(category)
                .code(code)
                .quantity(quantity)
                .errorCode(errorCode)
                .build();
    }

    public static Order createFromLine(String line) {
        if (line == null) {
            return Order.builder().build();
        } else if (line.length() < 35) {
            throw new IllegalArgumentException();
        }

        return Order.builder()
                .category(line.substring(4, 5))
                .code(line.substring(6, 7))
                .quantity(Integer.valueOf(line.substring(8, 9)))
                .build();
    }
}

NextCloudをUbuntu 22.04で動かす

前提条件

下記が導入済みであること
Apache 2
PHP 8.1
MariaDB

NextCloudのインストール

NextCloudのダウンロード

NextCloudのページからzipファイルをダウンロードする。

cd /var/www/html
wget https://download.nextcloud.com/server/releases/nextcloud-25.0.1.zip

NextCloudの配置

unzip nextcloud-25.0.1.zip
chown -R www-data:www-data /var/www/html/nextcloud

Apacheの設定

vi /etc/apache2/sites-available/nextcloud.conf
<Directory /var/www/html/nextcloud/>
  Require all granted
  Options FollowSymlinks MultiViews
  AllowOverride All
  <IfModule mod_dav.c>
    Dav off
  </IfModule>
</Directory>
sudo a2ensite nextcloud.conf
sudo a2enmod rewrite
sudo systemctl restart apache2
メモリエラーの解消
/etc/php/8.1/apache2/php.ini
memory_limit = 512M

Spring Boot 3にアップグレードする

目的

Spring Boot 3がリリースされたので、以前作成したプロジェクトをアップグレードしてみることにした。

主な変更点

主な変更点は「Spring Framework 6にアップグレードする」に記載したことと同じ。

実際に変更した箇所

依存関係

ライブラリの変更、またはバージョンアップを行った。

  • Spring Boot 3
  • hibernate-search-mapper-orm => hibernate-search-mapper-orm-orm6 6.1

コードの変更点

javaxをjakartaに書き換えただけの部分は省略する。

Spring Securityの設定箇所で@Configurationの追記が必要になった。

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {
    ↓
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {

まとめ

gradleを使用していれば、ほとんどの依存関係の更新も自動で行われるので、かなり簡単にアップグレードできた。

Spring Framework 6にアップグレードする

目的

Spring Framework 6がリリースされ、その他のパッケージも合わせて変更できそうだったので、勉強のために以前作ったプロジェクトをSpring Framework 6にアップグレードしてみることにした。

主な変更点

Jakarta EE 9への対応

大まかに言うとjavaxからjakartaに変わる。

import javax.servlet.http.HttpServletRequest;
    ↓
import jakarta.servlet.http.HttpServletRequest;

さらに、Jakarta EEに対応しているTomcat 10などに、デプロイする必要がある。

実際に変更した箇所

依存関係

ライブラリの変更、またはバージョンアップを行った。

  • greenmail 2
  • hibernate… 6.1
  • hibernate-search-mapper-orm => hibernate-search-mapper-orm-orm6 6.1
  • hibernate-validator 7
  • javax.el => jakarta.el 4
  • javax.servlet => jakarta.servlet 6
  • jakarta.mail 2
  • jakarta.xml.bind-api 3
  • jaxb-runtime 3
  • spring-core… 6
  • spring-data-jpa 3
  • spring-security… 6
  • thymeleaf-spring5 => thymeleaf-spring6 3.1
  • thymeleaf-extras-springsecurity5 => thymeleaf-extras-springsecurity6 3.1

コードの変更点

javaxをjakartaに書き換えただけの部分は省略する。

Spring MVC設定の変更

下記のようThymeleafのspring5パッケージをspring6に変更する。Resolver以外も同様に変更する。

    <bean id="templateResolver" class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">

MultipartResolverはStandardServletMultipartResolverを使用する。
web.xmlの<servlet>タグに<multipart-config>の設定を追加すると有効になる。

<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver" />

Thymeleaf Ver. 3.1にアップグレードも同時に行った場合は下記の変更も必要になる。
Thymeleaf 3.1にアップグレード

web.xmlの変更

下記の通り、変更する。

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0">

まとめ

javaxからjakartaに変更する箇所は多いが、結構簡単にアップグレード出来た。

Thymeleaf 3.1にアップグレード

公式に記載の通り、#request、#response、#session、#servletContextが使用できなくなった。

1.6 Removal of web-API based expression utility objects
The #request, #response, #session, and #servletContext are no longer available to expressions in Thymeleaf 3.1.

Thymeleaf 3.1: What’s new and how to migrate

下記の通り修正して対応した。

${#servletContext.getAttribute('assetsVersion')}
       ↓
${application.assetsVersion}
${#request.remoteUser}
       ↓
remoteUserをSessionに保持するようにして、
${session.remoteUser}

※ 2022/11/30 追記
Spring Securityを使用している場合、remoteUserを取得するなら、Sessionに保持は不要で、下記で可能だった。
${#authentication.name}

AWSのUbuntuを22.04.1にアップグレードする

AWS上のUbuntuをアップグレードしたときの手順を記録しておく。

注意)アップグレード後、phpが動かなくなってしまった。5の設定変更を実施したら再び動くようになった。

1.ファイアウォールの確認と設定

do-release-upgradeをリモートで実行し、アップグレード作業を行う場合は、1022 portの使用が推奨されている。ファイアウォールを実行している場合、事前にportの開放を行う。

sudo ufw allow 1022/tcp comment 'Temp open port ssh tcp port 1022 for upgrade'

2.セキュリティグループの設定

セキュリティグループのインバウンドルールに1022 portの許可を追加する。アップグレード作業にしか使わないportであるため、アクセス範囲は可能な限り狭く設定する。

3.アップグレードコマンドの実行

sudo do-release-upgrade

スクリプトの実行後、サーバーを再起動する。

4.Tera Termでログイン出来るようにする

Ubuntu 22ではssh-rsaが無効になっているため、Tera Termを使ってログインすることが出来ない。そこで下記のコマンドを実行して、ssh-rsaを有効にする。(Tera Termが使えないだけで、他の手段であればログイン出来る。Powershellや他のLinuxサーバーのsshコマンドでログインする。)

sudo su -
cp -p /etc/ssh/sshd_config /etc/ssh/sshd_config_org
sed -i '1s/^/PubkeyAcceptedAlgorithms=+ssh-rsa\n/' /etc/ssh/sshd_config
grep PubkeyAcceptedAlgorithms /etc/ssh/sshd_config
systemctl restart ssh.service

5.php8.1を有効にする

Ubuntu 22ではphp7.4が削除され、php 8.1が標準になっている。以下のコマンドでphp7.4の設定を削除して、php8.1を有効にする。

sudo a2dismod php7.4
sudo a2enmod php8.1
sudo service apache2 restart

6.セキュリティグループの設定

セキュリティグループのインバウンドルールの1022 portの許可を削除する。