Subversion(SVN)サーバーのIPやURLが変わった場合に、開発環境の設定を変更する覚書。
プロジェクト単位ではなく、レポジトリーで切り替える。
1.SVNレポジトリーブラウザでロケーションのプロパティを開く
2.レポジトリー・ロケーションの編集のURLの部分を変更する
※ID、パスワードの再入力が必要になるので、用意しておくこと。
Technical Notes
Subversion(SVN)サーバーのIPやURLが変わった場合に、開発環境の設定を変更する覚書。
プロジェクト単位ではなく、レポジトリーで切り替える。
1.SVNレポジトリーブラウザでロケーションのプロパティを開く
2.レポジトリー・ロケーションの編集のURLの部分を変更する
※ID、パスワードの再入力が必要になるので、用意しておくこと。
アップデートしたら下記のエラーが発生するようになった。
java.lang.NullPointerException: Cannot invoke "org.hibernate.boot.spi.MetadataImplementor.getEntityBindings()" because "this.metadata" is nullHibernate 6.3.0のバグっぽい。下記で修正中。
HHH-17154 Fix NullPointerException is thrown when constructing Entity…
同日にリリースされた6.2.8に変更したところ、NullPointerExceptionは出なくなったため、しばらくこちらを利用することにした。
理由があって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のバージョンが異なるため、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 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()は不要になった。
mockito-core-5.2.0.jar
JunitでMockを使用しているとき、下記のエラーが出る。何度も実行していると、たまに正常に終了するときもある。
Error creating bean with name 'userDao' defined in class path resource [common/service/applicationContext-test.xml]: Failed to instantiate [java.lang.Object]: Factory method 'mock' threw exception with message: Please don't pass any values here. Java will detect class automagically.
調べてみたら下記に理由が書いてあった。
automatically detect class to mock #2779
修正内容は以下の通り。
<bean id="userDao" class="org.mockito.Mockito" factory-method="mock">
- <constructor-arg value="common.dao.jpa.UserDao" />
+ <constructor-arg>
+ <value type="java.lang.Class">common.dao.jpa.UserDao</value>
+ </constructor-arg>
</bean>
Optionalについて、うまく使えていない気がするため、改めて整理してみる。
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."));
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チェックのミスも減るだろうか。
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."));
}
キーが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();
}
}
Spring Boot 3がリリースされたので、以前作成したプロジェクトをアップグレードしてみることにした。
主な変更点は「Spring Framework 6にアップグレードする」に記載したことと同じ。
ライブラリの変更、またはバージョンアップを行った。
javaxをjakartaに書き換えただけの部分は省略する。
Spring Securityの設定箇所で@Configurationの追記が必要になった。
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {
↓
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class WebSecurityConfig {
gradleを使用していれば、ほとんどの依存関係の更新も自動で行われるので、かなり簡単にアップグレードできた。
Spring Framework 6がリリースされ、その他のパッケージも合わせて変更できそうだったので、勉強のために以前作ったプロジェクトをSpring Framework 6にアップグレードしてみることにした。
大まかに言うとjavaxからjakartaに変わる。
import javax.servlet.http.HttpServletRequest;
↓
import jakarta.servlet.http.HttpServletRequest;
さらに、Jakarta EEに対応しているTomcat 10などに、デプロイする必要がある。
ライブラリの変更、またはバージョンアップを行った。
javaxをjakartaに書き換えただけの部分は省略する。
下記のよう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-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に変更する箇所は多いが、結構簡単にアップグレード出来た。