S2JDBC-GenのGen-Entityでエラー

# 概要
このページでまとめているエラーは、eclipseでSAStrutsを使ったウェブアプリケーションを作りたくて作業していた時に起きました。
ウェブアプリが動いて、次は宅内サーバーのDB(MariaDB)へアクセスできるようにしたいと思い、S2JDBCの利用を考えました。
S2JDBCのためには、まずS2JDBC-Genでエンティティを自動生成してもらうことが必要なようです。
プロジェクトはDoltengから作ったものなのでプロジェクトエクスプローラー等の、プロジェクト直下にs2jdbc-gen-build.xmlが作成されています。これを右クリックして、「実行」→「Ant ビルド...」を選び、gen-entityのみを選択して実行すれば手順としては問題ないはずですが、そこでエラーが起きました。

参考：[S2JDBC-Genのセットアップ](http://s2container.seasar.org/2.4/ja/s2jdbc_gen/setup.html)

## 環境
* OS：macOS Ventura 13.2.1(ハードはiMac 21.5-inch 2019だったり、MacBook Pro 13-inch, 2020)
* eclipse：2022-12 (4.26.0)

以下フレームワーク関連のバージョン

* struts：1.2.9
* sa-struts：1.0.4
* s2-tiger：2.4.46
* s2-framework：2.4.46
* s2-extension：2.4.46
* s2jdbc-gen：2.4.46
* freemarker：2.3.13

# 最初のつまずき
s2jdbc-gen-build.xmlをAntでビルドした結果のコンソールが以下。
```
Buildfile: /Volumes/Seagate_mac/ファイル/workspace/eclipse/wasabiapp/s2jdbc-gen-build.xml
gen-entity:
[gen-entity] Java Result: 1

BUILD FAILED
/Volumes/Seagate_mac/ファイル/workspace/eclipse/wasabiapp/s2jdbc-gen-build.xml:40: エラー: メイン・クラスを検出およびロードできませんでした
原因: java.lang.ClassNotFoundException: 
```

## 対応
「S2JDBC-Gen メイン・クラスが見つからなかったかロードできません」等で調べると、以下が近そうですね。

http://yakinikunotare.boo.jp/orebase2/s2/s2jdbc/s2jdbc_gen/error_main_class_not_found

こちらのページでは、特殊な環境向けの設定が悪さをしているとのこと。正直なところよくわかりませんが、私はmacOSなので全部UTF-8なのでは？と思い、特殊な環境向けの設定は必要ないと勝手に判断しました。
なので上記ページ通り、```<jvmarg value="${vmarg.encoding}"/>```を削除することでこちらのエラーは出なくなりました。
しかし、何も書かないのもなんか不安なので、少し触っていたところ、```<jvmarg value="-Dfile.encoding=UTF-8"/>```としてもエラーが出なくなったので、こちらの状態にしてあります。
```
<condition property="vmarg.encoding" value="-Dfile.encoding=UTF-8" else="">
  <isset property="eclipse.pdebuild.home"/>
</condition>
```
この部分がいらなくなっちゃうので、何も書かないでもよいかなと思います。そもそもこれがなぜ上手くいかないのかはわかれば追記します。





## 結果
以下の対応で当該のエラーは出なくなりました。
```diff_xml
classpathref="classpath">
-   <jvmarg value="${vmarg.encoding}"/>
+   <jvmarg value="-Dfile.encoding=UTF-8"/>
</gen-entity>
```

# 2度目のつまずき
その後、s2jdbc-gen-build.xmlをAntでビルドした結果のコンソールが以下(長いのでエラーより前は途中から記載)。
```
...
[gen-entity] DEBUG 2023-03-09 21:11:23,177 [main] S2Containerを作成します。path=creator.dicon
[gen-entity] DEBUG 2023-03-09 21:11:23,193 [main] S2Containerを作成しました。path=creator.dicon
[gen-entity] DEBUG 2023-03-09 21:11:23,198 [main] S2Containerを作成しました。path=warmdeploy.dicon
[gen-entity] Java Result: 1

BUILD FAILED
/Users/wasabi/Documents/workspace/eclipse/wasabiapp/s2jdbc-gen-build.xml:40: Exception in thread "main" java.lang.ExceptionInInitializerError
	at org.seasar.framework.util.ResourcesUtil.getResourcesTypes(ResourcesUtil.java:163)
	at org.seasar.framework.convention.impl.NamingConventionImpl.addExistChecker(NamingConventionImpl.java:895)
	at org.seasar.framework.convention.impl.NamingConventionImpl.addRootPackageName(NamingConventionImpl.java:428)
	at org.seasar.framework.convention.impl.NamingConventionImpl.addRootPackageName(NamingConventionImpl.java:410)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.seasar.framework.util.MethodUtil.invoke(MethodUtil.java:96)
	at org.seasar.framework.beans.impl.BeanDescImpl.invoke(BeanDescImpl.java:218)
	at org.seasar.framework.container.assembler.AbstractMethodAssembler.invoke(AbstractMethodAssembler.java:132)
	at org.seasar.framework.container.assembler.AbstractMethodAssembler.invoke(AbstractMethodAssembler.java:96)
	at org.seasar.framework.container.assembler.DefaultInitMethodAssembler.assemble(DefaultInitMethodAssembler.java:49)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.assemble(SingletonComponentDeployer.java:69)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.deploy(SingletonComponentDeployer.java:48)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.init(SingletonComponentDeployer.java:76)
	at org.seasar.framework.container.impl.ComponentDefImpl.init(ComponentDefImpl.java:236)
	at org.seasar.framework.container.impl.S2ContainerImpl.init(S2ContainerImpl.java:563)
	at org.seasar.framework.container.impl.S2ContainerImpl.init(S2ContainerImpl.java:560)
	at org.seasar.framework.container.impl.S2ContainerImpl.init(S2ContainerImpl.java:560)
	at org.seasar.framework.container.factory.S2ContainerFactory.configure(S2ContainerFactory.java:234)
	at org.seasar.framework.container.factory.S2ContainerFactory.configure(S2ContainerFactory.java:211)
	at org.seasar.framework.container.factory.S2ContainerFactory.<clinit>(S2ContainerFactory.java:116)
	at org.seasar.framework.container.factory.SingletonS2ContainerFactory.init(SingletonS2ContainerFactory.java:152)
	at org.seasar.extension.jdbc.gen.internal.util.SingletonS2ContainerFactorySupport.init(SingletonS2ContainerFactorySupport.java:61)
	at org.seasar.extension.jdbc.gen.internal.command.AbstractCommand.init(AbstractCommand.java:201)
	at org.seasar.extension.jdbc.gen.internal.command.AbstractCommand.execute(AbstractCommand.java:160)
	at org.seasar.extension.jdbc.gen.internal.command.CommandInvokerImpl.invoke(CommandInvokerImpl.java:29)
	at org.seasar.extension.jdbc.gen.command.CommandAdapter.main(CommandAdapter.java:61)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.findLoadedClass(java.lang.String) accessible: module java.base does not "opens java.lang" to unnamed module @29428716
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
	at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
	at org.seasar.framework.util.ClassLoaderUtil.getFindLoadedClassMethod(ClassLoaderUtil.java:50)
	at org.seasar.framework.util.ClassLoaderUtil.<clinit>(ClassLoaderUtil.java:35)
	... 29 more

Total time: 1 second
```

## 対応
出ているエラー(「java.lang.ExceptionInInitializerError」「java.lang.reflect.InaccessibleObjectException」「module java.base does not "opens java.lang" to unnamed module」)等で調べると以下のページがかなり近そうです。

https://blog1.mammb.com/entry/2020/12/28/090000

引数に```--add-opens java.base/jdk.internal.loader=ALL-UNNAMED```をつけるとのこと。
さて、今回はAntでの実行ですし、javaコマンドじゃないのでどうアプローチしようかと考えました。「Ant ビルド...」を選んで、メインタブから引数を指定しても変わらず。
[こちらのページ](https://qiita.com/kagamihoge/items/0fb7f453e95ad320ddaf)にあるように、eclipse.iniに指定しても変わらず(そもそも初めから```--add-opens java.base/java.lang=ALL-UNNAMED```の行がありますしね...)。
そういえば、さっきのつまずき中に、「jvmargはjvmに引数を渡している」とのような記述を見かけた気がします。
```
<jvmarg value="--add-opens=java.base/java.lang=ALL-UNNAMED" />
```
こちらを先ほどと同じところに追加してみました。すると無事、当該のエラーは出なくなりました。

## 結果
以下の対応で当該のエラーは出なくなりました。
```diff_xml
classpathref="classpath">
   <jvmarg value="-Dfile.encoding=UTF-8"/>
+  <jvmarg value="--add-opens=java.base/java.lang=ALL-UNNAMED" />
</gen-entity>
```

# 3度目のつまずき
その後、s2jdbc-gen-build.xmlをAntでビルドした結果のコンソールが以下。
そろそろ、私の環境が悪いだけで、入れ直したりすればいけるんじゃないかと疑います。とはいえ、好転してそうな気がするので、頑張りましょう。

```
...
[gen-entity] DEBUG 2023-03-10 11:02:10,690 [main] S2Containerを作成しました。path=warmdeploy.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,703 [main] S2Containerを作成します。path=s2jdbc.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,705 [main] S2Containerを作成します。path=jdbc.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,707 [main] S2Containerを作成します。path=jta.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,717 [main] S2Containerを作成しました。path=jta.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,727 [main] S2Containerを作成しました。path=jdbc.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,727 [main] S2Containerを作成します。path=s2jdbc-internal.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,729 [main] S2Containerを作成します。path=convention.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,731 [main] S2Containerを作成しました。path=convention.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,734 [main] S2Containerを作成しました。path=s2jdbc-internal.dicon
[gen-entity] DEBUG 2023-03-10 11:02:10,751 [main] S2Containerを作成しました。path=s2jdbc.dicon
[gen-entity] Java Result: 1

BUILD FAILED
/Volumes/Seagate_mac/ファイル/workspace/eclipse/wasabiapp/s2jdbc-gen-build.xml:40: Exception in thread "main" org.seasar.framework.beans.IllegalPropertyRuntimeException: [ESSR0059]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)のプロパティ(driverClassName)の設定に失敗しました。理由はorg.seasar.framework.exception.SIllegalArgumentException: [ESSR0098]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)[jdk.internal.loader.ClassLoaders$AppClassLoader@5ffd2b27]の型(java.lang.String)[null]のプロパティ(driverClassName)に、型(java.lang.String)[null]の値(com.mysql.jdbc.Driver)を設定できませんでした。対象のクラスは(org.seasar.extension.dbcp.impl.XADataSourceImpl)[jdk.internal.loader.ClassLoaders$AppClassLoader@5ffd2b27]です。
	at org.seasar.framework.beans.impl.PropertyDescImpl.setValue(PropertyDescImpl.java:279)
	at org.seasar.framework.container.assembler.AbstractBindingTypeDef.setValue(AbstractBindingTypeDef.java:321)
	at org.seasar.framework.container.assembler.AbstractBindingTypeDef.bindManual(AbstractBindingTypeDef.java:127)
	at org.seasar.framework.container.assembler.AbstractBindingTypeDef.bind(AbstractBindingTypeDef.java:74)
	at org.seasar.framework.container.assembler.AccessTypePropertyDef.bind(AccessTypePropertyDef.java:50)
	at org.seasar.framework.container.assembler.AccessTypePropertyDef.bind(AccessTypePropertyDef.java:41)
	at org.seasar.framework.container.assembler.AutoPropertyAssembler.assemble(AutoPropertyAssembler.java:56)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.assemble(SingletonComponentDeployer.java:68)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.deploy(SingletonComponentDeployer.java:48)
	at org.seasar.framework.container.deployer.SingletonComponentDeployer.init(SingletonComponentDeployer.java:76)
	at org.seasar.framework.container.impl.ComponentDefImpl.init(ComponentDefImpl.java:236)
	at org.seasar.framework.container.impl.S2ContainerImpl.init(S2ContainerImpl.java:563)
	at org.seasar.framework.container.impl.ThreadSafeS2ContainerImpl.init(ThreadSafeS2ContainerImpl.java:172)
	at org.seasar.framework.container.impl.S2ContainerImpl.init(S2ContainerImpl.java:560)
	at org.seasar.framework.container.impl.ThreadSafeS2ContainerImpl.init(ThreadSafeS2ContainerImpl.java:172)
	at org.seasar.framework.container.factory.SingletonS2ContainerFactory.init(SingletonS2ContainerFactory.java:167)
	at org.seasar.extension.jdbc.gen.internal.util.SingletonS2ContainerFactorySupport.init(SingletonS2ContainerFactorySupport.java:61)
	at org.seasar.extension.jdbc.gen.internal.command.AbstractCommand.init(AbstractCommand.java:201)
	at org.seasar.extension.jdbc.gen.internal.command.AbstractCommand.execute(AbstractCommand.java:160)
	at org.seasar.extension.jdbc.gen.internal.command.CommandInvokerImpl.invoke(CommandInvokerImpl.java:29)
	at org.seasar.extension.jdbc.gen.command.CommandAdapter.main(CommandAdapter.java:61)
Caused by: org.seasar.framework.exception.SIllegalArgumentException: [ESSR0098]クラス(org.seasar.extension.dbcp.impl.XADataSourceImpl)[jdk.internal.loader.ClassLoaders$AppClassLoader@5ffd2b27]の型(java.lang.String)[null]のプロパティ(driverClassName)に、型(java.lang.String)[null]の値(com.mysql.jdbc.Driver)を設定できませんでした。対象のクラスは(org.seasar.extension.dbcp.impl.XADataSourceImpl)[jdk.internal.loader.ClassLoaders$AppClassLoader@5ffd2b27]です。
	at org.seasar.framework.beans.impl.PropertyDescImpl.setValue(PropertyDescImpl.java:258)
	... 20 more
Caused by: org.seasar.framework.exception.ClassNotFoundRuntimeException: [ESSR0044]クラスが見つかりませんでした。詳細はjava.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	at org.seasar.framework.util.ClassUtil.forName(ClassUtil.java:100)
	at org.seasar.framework.util.DriverManagerUtil.registerDriver(DriverManagerUtil.java:41)
	at org.seasar.extension.dbcp.impl.XADataSourceImpl.setDriverClassName(XADataSourceImpl.java:77)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.seasar.framework.util.MethodUtil.invoke(MethodUtil.java:96)
	at org.seasar.framework.beans.impl.PropertyDescImpl.setValue(PropertyDescImpl.java:251)
	... 20 more
Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:467)
	at org.seasar.framework.util.ClassUtil.forName(ClassUtil.java:98)
	... 28 more

Total time: 938 milliseconds
```
長いのでエラーより前は途中から記載ですが、エラーの前を少し多めに記載しました。
というのも、2度目のつまずきでは```[gen-entity] DEBUG 2023-03-10 11:02:10,690 [main] S2Containerを作成しました。path=warmdeploy.dicon```の直後にエラーが出てましたが、今回はそのさらに後です。先ほども言いましたが、状況は好転している気がします。

## 対応
すいません、```[jdk.internal.loader.ClassLoaders$AppClassLoader@5ffd2b27]の型(java.lang.String)[null]のプロパティ(driverClassName)に、型(java.lang.String)[null]の値(com.mysql.jdbc.Driver)を設定できませんでした。```と書いてありますね。
jdbc.diconの```driverClassName```を変更していませんでした。```URL```やら```user```やらは修正していたんですけどね。

## 結果
環境依存になりますので、記載はしません。```jdbc.dicon```を環境に合わせて修正忘れずに、ということです。

# 4度目のつまずき(1, 2度目のつまずきと同様)
```<jvmarg value="${vmarg.encoding}"/>```を使っている他の行も、1, 2度目のつまずき同様に、以下のように修正していきます。
```diff_xml
-  <jvmarg value="${vmarg.encoding}"/>
+  <jvmarg value="-Dfile.encoding=UTF-8"/>
+  <jvmarg value="--add-opens=java.base/java.lang=ALL-UNNAMED" />
```

# 最後に
戦いの末、ようやくS2JDBC-Genが動き、エンティティを作成してもらえました！！！
そもそも、Java1.7以下であれば起きないエラーも多いので、かなり環境依存ですが、1.8以上で同様のエラーが起きた方向けに書いてます。
現状の記載内容に関してもし詳細がわかれば随時、しっかりとした原因や正しい対策など追記したいと思います。
何か、間違っているところ、詳細、原因などコメントありましたら、よろしくお願いいたします。