Spring IOのport out of range -1エラー

Spring Tools Suite、またはそのプラグインを入れたEclipseで「Failed to fetch Generation from Spring IO: port out of range:-1」というエラーが表示される場合、以下の設定を変更することでエラー解消することが出来る。

Eclipseの設定を開き、一般>ネットワーク接続のアクティブ・プロバイダーを「直接」に変更する。

Tomcat 10をJava 21で動かす

UbuntuにTomcat 10をインストールしJava 21で動かしたいが、まだパッケージでは提供されていないため、手動でインストールする。

前提

Java 21がインストール済みであること。
インストールされていない場合はこちらの記事の「Java 21のインストール」を参照のこと。

$ java -version
openjdk version "21" 2023-09-19 LTS
OpenJDK Runtime Environment Temurin-21+35 (build 21+35-LTS)
OpenJDK 64-Bit Server VM Temurin-21+35 (build 21+35-LTS, mixed mode, sharing)

Tomcat 10のインストール

Apache Tomcatのページがら最新版をダウンロードして解凍する。今後のため、シンボリックリンクも作成しておく。

$ wget https://dlcdn.apache.org/tomcat/tomcat-10/v10.1.15/bin/apache-tomcat-10.1.15.tar.gz
$ tar -xvf apache-tomcat-10.1.15.tar.gz
$ ln -s /opt/apache-tomcat-10.1.15 /opt/tomcat-10

Tomcat 10用の実行ユーザーを作成する。

$ sudo useradd -m -U -d /opt/tomcat-10 -s /bin/false tomcat1$

ファイルのオーナーを先ほど作成したユーザーに変更する。

$ sudo chown -R tomcat10: /opt/apache-tomcat-10.1.15

シェルを実行可能にする。

$ sudo sh -c 'chmod +x /opt/tomcat-10/bin/*.sh'

SystemD用のファイルを作成する。

$ sudo vi /usr/lib/systemd/system/tomcat10.service
#
# Systemd unit file for Apache Tomcat
#
[Unit]
Description=Apache Tomcat 10 Web Application Server
After=network.target
[Service]
# Configuration
Environment="JAVA_HOME=/opt/jdk-21"
Environment="JAVA_OPTS=-Djava.awt.headless=true"
Environment="CATALINA_BASE=/opt/tomcat-10"
Environment="CATALINA_HOME=/opt/tomcat-10"
Environment="CATALINA_OPTS=-Xms128m -Xmx1024m -server"
Environment="CATALINA_PID=/opt/tomcat-10/temp/tomcat.pid"
# Lifecycle
Type=forking
ExecStart=/opt/tomcat-10/bin/startup.sh
ExecStop=/opt/tomcat-10/bin/shutdown.sh
Restart=always
PIDFile=/opt/tomcat-10/temp/tomcat.pid
# Logging
SyslogIdentifier=tomcat10
# Security
User=tomcat10
Group=tomcat10
[Install]
WantedBy=multi-user.target

SystemDの設定ファイルを再読み込みする。

$ sudo systemctl daemon-reload

Tomcat 10を有効にし、起動する。

$ sudo systemctl enable --now tomcat10
Created symlink /etc/systemd/system/multi-user.target.wants/tomcat10.service  /lib/systemd/system/tomcat10.service.

正常起動を確認する。

$ sudo systemctl status tomcat10
 tomcat10.service - Apache Tomcat 10 Web Application Server
     Loaded: loaded (/lib/systemd/system/tomcat10.service; enabled; vendor pres>
     Active: active (running) since Wed 2023-10-25 05:58:29 UTC; 6s ago
    Process: 12815 ExecStart=/opt/tomcat-10/bin/startup.sh (code=exited, status>
   Main PID: 12822 (java)
      Tasks: 35 (limit: 18910)
     Memory: 101.2M
        CPU: 6.485s
     CGroup: /system.slice/tomcat10.service
             mq12822 /opt/jdk-21/bin/java -Djava.util.logging.config.file=/opt/>
10月 25 05:58:29 hostname systemd[1]: Starting Apache Tomcat 10 Web Applicati>
10月 25 05:58:29 hostname tomcat10[12815]: Tomcat started.
10月 25 05:58:29 hostname systemd[1]: Started Apache Tomcat 10 Web Applicatio>

JenkinsをJava 21で動かす

JenkinsでビルドしていたプロジェクトをJava 21に変更したため、JenkinsもJava 21で起動する必要がある。

前提

Ubuntu上で既にJenkinsがJava 17で稼働しているものとする。
Jenkinsは最新版を使用している。

Java 21のインストール

下記からLinux用JDKをダウンロードして、任意のフォルダに解凍する。
https://adoptium.net/temurin/releases/

$ cd /opt
$ sudo wget https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21%2B35/OpenJDK21U-jdk_x64_linux_hotspot_21_35.tar.gz
$ sudo tar -xvf OpenJDK21U-jdk_x64_linux_hotspot_21_35.tar.gz
$ sudo ln -s /opt/jdk-21+35 /opt/jdk-21

Javaの設定

$ sudo update-alternatives --install /usr/bin/java java /opt/jdk-21/bin/java 1
$ sudo update-alternatives --install /usr/bin/javac javac /opt/jdk-21/bin/javac 1
$ sudo update-alternatives --install /usr/bin/javadoc javadoc /opt/jdk-21/bin/javadoc 1

インストールしたJDKをデフォルトに設定する。

$ sudo update-alternatives --config java
$ sudo update-alternatives --config javac
$ sudo update-alternatives --config javadoc

Java 21に切り替わっていることを確認する。

$ java -version
openjdk version "21" 2023-09-19 LTS
OpenJDK Runtime Environment Temurin-21+35 (build 21+35-LTS)
OpenJDK 64-Bit Server VM Temurin-21+35 (build 21+35-LTS, mixed mode, sharing)

Jenkinsの起動と動作確認

Jenkinsを起動する。

$ sudo systemctl start jenkins
$ sudo systemctl status jenkins
 jenkins.service - Jenkins Continuous Integration Server
・・・
10月 19 04:37:40 systemd[1]: Started Jenkins Continuous Integration >

Java 21のプロジェクトをビルドし、正常終了することを確認する。

Java 17から21にアップグレードする

Java 21がリリースされたので、自前のプログラムをJava 21に変更してみる。

コードの変更部分

Java 21に変更したら下記のコードが非推奨になっていた。

new Locale(locale)
new URL(referer)

それぞれ下記の通り修正した。

Locale.of(locale)
URI.create(referer).toURL()

newするようなコードは今後もなくなっていくのだろうか。個人的には変更後のコードの方が好きだ。

2023/10/17 追記

pom.xmlに下記設定を追記した。

-proc:full

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <compilerArgs>
            <arg>-parameters</arg>
            <arg>-proc:full</arg>
        </compilerArgs>
    </configuration>
</plugin>

-XX:+EnableDynamicAgentLoading

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.1.2</version>
    <configuration>
        <reuseForks>false</reuseForks>
        <argLine>${argLine} -XX:+EnableDynamicAgentLoading -Dcatalina.base=${project.build.directory}</argLine>
    </configuration>
</plugin>
2023/10/18 追記

Java 21にコンパイルの設定を変更すると、なぜか下記11行目ががコンパイルエラーになる。(一部抜粋)

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class NovelProcessTest {

    @Mock
    private MessageSourceAccessor messages;

    @Test
    void testExecute() {
        when(messages.getMessage(anyString())).thenReturn(

下記のように修正するとコンパイルエラーは解消する。理由はわからない。

import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class NovelProcessTest {

    @Mock
    private MessageSourceAccessor messages;

    @Test
    void testExecute() {
        String msg = messages.getMessage(anyString());
        when(msg).thenReturn(

下記でもコンパイルエラーは解消するので、whenがメソッドの最初にあるのが駄目なのだろうか。

    @Test
    void testExecute() {
        String msg = "";
        when(messages.getMessage(anyString())).thenReturn(

jjwt 0.11.5から0.12.1にアップグレードする

以下のメソッドを変更した。

- SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
+ SecretKey key = Jwts.SIG.HS512.key().build();

- Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
+ Claims claims = Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload();

Jwts.builder()
-        .setClaims(claims)
-        .setSubject(username)
-        .setIssuedAt(createdDate)
-        .setExpiration(expirationDate)
+        .claims(claims)
+        .subject(username)
+        .issuedAt(createdDate)
+        .expiration(expirationDate)
        .signWith(key)
        .compact();

0.12.0はバグがあるので注意!
https://github.com/jwtk/jjwt/issues/854

class io.jsonwebtoken.impl.lang.OptionalMethodInvoker cannot access class sun.security.util.KeyUtil (in module java.base) because module java.base does not export sun.security.util to unnamed module

Eclipse上でSVNのURLを切り替える

Subversion(SVN)サーバーのIPやURLが変わった場合に、開発環境の設定を変更する覚書。

プロジェクト単位ではなく、レポジトリーで切り替える。

1.SVNレポジトリーブラウザでロケーションのプロパティを開く

2.レポジトリー・ロケーションの編集のURLの部分を変更する

※ID、パスワードの再入力が必要になるので、用意しておくこと。

Hibernate 6.3.0にアップデートしたらNullPointerExceptionが発生した

アップデートしたら下記のエラーが発生するようになった。

java.lang.NullPointerException: Cannot invoke "org.hibernate.boot.spi.MetadataImplementor.getEntityBindings()" because "this.metadata" is null

Hibernate 6.3.0のバグっぽい。下記で修正中。

HHH-17154 Fix NullPointerException is thrown when constructing Entity…

同日にリリースされた6.2.8に変更したところ、NullPointerExceptionは出なくなったため、しばらくこちらを利用することにした。

依存関係にあるJarファイルを1つのフォルダにまとめる

理由があってJarファイルをまとめてコピーしておく必要があるとき、Mavenビルドのプラグインに以下を追加すると、指定したフォルダに依存関係のあるJarファイルを全て出力することが出来る。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        <excludeGroupIds>org.apache.maven.surefire</excludeGroupIds>
                    </configuration>
                </execution>
            </executions>
        </plugin>

Hibernate ORM 6.2とHibernate Search 6.1とjandexについて

Hibernate ORM 6.2とHibernate Search 6.1を同時に使用する場合、それぞれの依存関係に設定されているjandexのバージョンが異なるため、method not foundエラーが発生する。(実験的な互換性らしい)

エラーが発生した場合は、以下の通りHibernate Searchの方にjandexを除外する設定を追記する。

<dependency>
    <groupId>org.hibernate.search</groupId>
    <artifactId>hibernate-search-mapper-orm-orm6</artifactId>
    <version>${hibernate-search.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.jboss</groupId>
            <artifactId>jandex</artifactId>
        </exclusion>
    </exclusions>
</dependency>

2023/7/11 追記:Hibernate Search 6.2がリリースされ、上記の対応は不要になった。

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

Spring Bootの3.1がリリースされたため、早速アップグレードしたところ、Spring Securityのバージョンが6.1になっていた。

その結果、このクラスでは以下のメソッドが非推奨になった。
.exceptionHandling()
.cors()
.csrf()
.formLogin()
.httpBasic()
.authorizeExchange()
.and()

@Bean
public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) {
    return http
            .exceptionHandling()
            .authenticationEntryPoint((swe, e) -> {
                return Mono.fromRunnable(() -> {
                    swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                });
            }).accessDeniedHandler((swe, e) -> {
                return Mono.fromRunnable(() -> {
                    swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                });
            }).and()
            .cors().configurationSource(corsConfigurationSource())
            .and().csrf().disable()
            .formLogin().disable()
            .httpBasic().disable()
            .authenticationManager(authenticationManager)
            .securityContextRepository(securityContextRepository)
            .authorizeExchange()
            .pathMatchers(HttpMethod.OPTIONS).permitAll()
            .pathMatchers("/crawler-api/login").permitAll()
            .pathMatchers("/crawler-api/signup").permitAll()
            .pathMatchers("/crawler-api/users*").hasAuthority("ROLE_USER")
            .pathMatchers("/crawler-api/novels*").hasAuthority("ROLE_USER")
            .anyExchange().authenticated()
            .and().build();
}

非推奨になったメソッドには新たにラムダ式で記述出来る同名のメソッドが追加されている。今後はそちらを使用する。

新しいメソッドに置き換えると下記の通りとなる。

.exceptionHandling(exceptionHandling -> exceptionHandling
        .authenticationEntryPoint((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            });
        }).accessDeniedHandler((swe, e) -> {
            return Mono.fromRunnable(() -> {
                swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
            });
        }))
.csrf(csrf -> csrf.disable())
.formLogin(formLogin -> formLogin.disable())
.httpBasic(httpBasic -> httpBasic.disable())
.authenticationManager(authenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange(exchanges -> exchanges
        .pathMatchers(HttpMethod.OPTIONS).permitAll()
        .pathMatchers("/crawler-api/login").permitAll()
        .pathMatchers("/crawler-api/signup").permitAll()
        .pathMatchers("/crawler-api/users*").hasAuthority("ROLE_USER")
        .pathMatchers("/crawler-api/novels*").hasAuthority("ROLE_USER")
        .anyExchange().authenticated())
.build();

cors()についてはCorsConfigurationSourceのBeanが定義されていれば自動的に読み込まれるとのことなので、SecurityWebFilterChainからは削除した。
また、新しいメソッドを置き換えた結果、.and()は不要になった。