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

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

実行

1
npm run dev

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

開発環境

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

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

目的

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

要件

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

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

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

実装例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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」宣言を使える箇所があるが、わかりやすくするため、あえて通常の宣言にしている。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@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ファイルをダウンロードする。

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

NextCloudの配置

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

Apacheの設定

1
2
3
4
5
6
7
8
9
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>
1
2
3
sudo a2ensite nextcloud.conf
sudo a2enmod rewrite
sudo systemctl restart apache2
メモリエラーの解消
1
2
/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の追記が必要になった。

1
2
3
4
5
6
7
8
@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に変わる。

1
2
3
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以外も同様に変更する。

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

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

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

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

web.xmlの変更

下記の通り、変更する。

まとめ

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

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

1
2
3
${#servletContext.getAttribute('assetsVersion')}
       ↓
${application.assetsVersion}
1
2
3
4
5
6
7
8
${#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の開放を行う。

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

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

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

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

1
sudo do-release-upgrade

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

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

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

1
2
3
4
5
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を有効にする。

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

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

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

NovelAIを使ってみる

最近話題のNovelAIを使ってみる。

ユーザー登録する

https://novelai.net/

上記ページにアクセスして、ユーザー登録を行う。画像の生成には後述のサブスク登録が必要になるため、ユーザー登録が必要になる。

サブスク登録する

サブスク登録していないと、画像の生成機能を使用することが出来ない。10ドルのプランで1000 Anlas分使用できる。Nomalサイズの画像生成で5 Anlas必要になるため、このプランでは毎月200枚ほど生成出来る計算になる。足りないなら後でプランをアップグレードしたり、Anlasだけ買い足したりすることが出来るので、お試しであれば10ドルのプランで良いと思う。

画像を生成する

Image Generationをクリックする。

Enter your prompt here. に文字を入力する。(英語)

自分が試しに入力した文字は下記の通り。
masterpiece, highest quality, high quality, cyberpunk,
上記とは別にUndesired Contentに以下の文字列を入力した。
lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, artist name

生成された画像は下記の通り。

簡単にきれいな画像を生成することは出来るが、一発でほしいものが出るとは限らない。設定を変えながら何度も生成することになるので、トークンの減りが激しい。

変数のコピー元を変更するとコピー先が変わる場合と変わらない場合

Javaにおいて変数のコピー先が変わらない例

1
2
3
4
5
6
7
8
String a = "りんご"// (1)
String b = a; // 変数bにaを代入 (2)
System.out.println(a);
System.out.println(b);
 
実行結果
りんご
りんご
1
2
3
4
5
6
7
8
// 変数aに値を設定し直す
a = "みかん"// (3)
System.out.println(a);
System.out.println(b);
 
実行結果
みかん
りんご

変数aは「みかん」に置き換わるが、変数bは変わらない。これは下記のようなイメージ。

 (1) String a = “りんご”のイメージ
  「りんご」の写真を撮り、その写真をaという箱に入れる。

 (2) String a = bのイメージ
  aの箱に入っている「りんご」の写真のコピーを取り、そのコピーをbの箱に入れる。

 (3) a = “みかん”のイメージ
  aの箱に入っていた「りんご」の写真を捨てて、aの箱に「みかん」の写真を入れる。

コピーを取ってbの箱に入れた「りんご」の写真はそのままなので、System.out.println(b)は「りんご」となる。

コピー先が変わる例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 配列を作成する
String[] c = new String[] { "りんご", "バナナ" };  // (1)
String[] d = c;  // (2)
 
System.out.println(d[0]);
System.out.println(d[1]);
 
c[0] = "みかん"// (3)
 
System.out.println(d[0]);
System.out.println(d[1]);
 
実行結果
りんご
バナナ
みかん
バナナ

配列の要素c[0]を変更すると、d[0]の内容も変わる。これは下記のようなイメージ。

 (1) String[] c = new String[] { “りんご”, “バナナ” }のイメージ
  「りんご」の写真を撮って、cという棚の[0]という箱に入れる。
  「バナナ」の写真を撮って、cという棚の[1]という箱に入れる。

 (2) String[] d = cのイメージ
  cの棚の箱を全て取り出して、中身をコピーするのは大変なので(今回は2個だけだが、通常はもっと多いことを想定している)、
  dという棚の[0]の位置にcという棚の[0]を見るようにとメモを置く。
  dという棚の[1]の位置にcという棚の[1]を見るようにとメモを置く。

 (3) c[0] = “みかん”のイメージ
  「みかん」の写真を撮って、cという棚の[0]という箱に入れる。

変数dには値のコピーはなく、参照先の情報しかないため、System.out.println(d[0])とするとc[0]の情報を参照することになり、コピー元と同様に表示される値が変わることになる。

同様に変数cとdを逆にしても、表示される値が変わる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String[] c = new String[] { "りんご", "バナナ" };
String[] d = c;
 
System.out.println(c[0]);
System.out.println(c[1]);
 
d[0] = "みかん"; // コピー先を書き換える (4)
 
System.out.println(c[0]);
System.out.println(c[1]);
 
実行結果
りんご
バナナ
みかん
バナナ

 (4) d[0] = “みかん”のイメージ
  「みかん」の写真を撮って、dという棚の[0]という箱に入れようとしたら、そこに箱はなく、cという棚の[0]を見るようにとメモがあったので、cという棚の[0]の箱の中に「みかん」の写真を入れる。

(4)の操作でc[0]の内容が変わってしまっているので、System.out.println(c[0])の出力も「みかん」になる。

EclipseでGitの作成者、コミッターの初期設定を変更する

EclipseでGitを使用するとき、作成者、コミッターの欄に、OSのユーザー、コンピューター名が設定されてしまう。コミットする度に修正するのは面倒なので、最初からほしい名称が表示されるようにしたい。

Eclipseの設定のGit関連の設定項目の中から「構成」を選択する。エントリーの追加をクリックし、以下の項目を追加する。

user.name

user.email

Eclipseを一度再起動すると、先ほど設定した値が作成者、コミッターの欄に表示されるようになる。