Eclipseでビルドした.warファイルをEC2上に配置して実行してみるとUnsupportedClassVersionErrorが出力された


環境

実行環境

AWS EC2 t2.micro
Amazon Linux 2
Apache Tomcat/9.0.34

# java -version
openjdk version "1.8.0_252-debug"
OpenJDK Runtime Environment (build 1.8.0_252-debug-b09)
OpenJDK 64-Bit Server VM (build 25.252-b09-debug, mixed mode)

# javac -version
javac 1.8.0_252-debug

ビルド環境(ローカル環境)

Windows10
Eclipse 2020-03

Windowsコマンドプロンプトで確認したJavaのバージョン

> java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

> javac -version
javac 1.8.0_231

経緯

Webアプリ開発の勉強のため、Eclipse上で動的Webプロジェクト「example」を作成し、
サーブレット&JSPでサンプルアプリケーション「HealthCheck」を作成した。
ブラウザから「http://localhost:8080/example/HealthCheck」で正常動作を確認。
ローカルではちゃんと動く。

EC2にデプロイして動くか確認したかったため、Eclipse上でプロジェクトごと
.warファイルにエクスポートしEC2上へ配置。

[root@ip-10-0-0-50 webapps]# ls -l /opt/apache-tomcat/webapps
total 24
drwxr-x--- 16 tomcat tomcat  4096 Apr 24 15:28 docs
drwxr-x---  5 tomcat tomcat   123 Jun  3 12:55 example
drwxr-x---  6 tomcat tomcat    83 Apr 24 15:28 examples
-rwxr-xr-x  1 tomcat tomcat 18910 Jun  3 12:55 example.war        ←★
drwxr-x---  5 tomcat tomcat    87 Apr 24 15:28 host-manager
drwxr-x---  5 tomcat tomcat   103 Apr 24 15:28 manager
drwxr-x---  3 tomcat tomcat   283 Apr 24 15:28 ROOT

[root@ip-10-0-0-50 webapps]# ls -l /opt/apache-tomcat/webapps/example
total 16
drwxr-x--- 2 tomcat tomcat  31 Jun  3 12:55 css
-rw-r----- 1 tomcat tomcat 761 May 27 00:08 Ex5_2.jsp
-rw-r----- 1 tomcat tomcat 493 May 26 23:31 formSample.jsp
-rw-r----- 1 tomcat tomcat 140 May 25 21:06 hello.html
drwxr-x--- 2 tomcat tomcat  44 Jun  3 12:55 META-INF
-rw-r----- 1 tomcat tomcat 670 May 26 18:38 sample.jsp
drwxr-x--- 5 tomcat tomcat  43 Jun  3 12:55 WEB-INF

tomcat.serviceのリスタートを行い、ステータスチェック

[root@ip-10-0-0-50 webapps]# systemctl status tomcat.service
● tomcat.service - Apache Tomcat Web Application Container
   Loaded: loaded (/usr/lib/systemd/system/tomcat.service; enabled; vendor preset: disabled)
   Active: active (exited) since Wed 2020-06-03 13:12:27 UTC; 1h 4min ago
  Process: 4100 ExecStop=/opt/apache-tomcat/bin/shutdown.sh (code=exited, status=0/SUCCESS)
  Process: 4129 ExecStart=/opt/apache-tomcat/bin/startup.sh (code=exited, status=0/SUCCESS)
 Main PID: 4129 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/tomcat.service
           mq4144 /usr/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat/conf/loggin...

Jun 03 13:12:27 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Starting Apache Tomca...
Jun 03 13:12:27 ip-10-0-0-50.ap-northeast-1.compute.internal systemd[1]: Started Apache Tomcat...
Hint: Some lines were ellipsized, use -l to show in full.

発生事象

EC2にアタッチしたEIP宛てにブラウザから接続したところ「500 – Internal Server Error」が発生
http://[EIP]:8080/example/HealthCheck


HTTPステータス 500 – Internal Server Error

タイプ: 例外報告
メッセージ: Error instantiating servlet class [servlet.HealthCheck]
説明:サーバーは予期しない条件に遭遇しました。それはリクエストの実行を妨げます。
例外:
 javax.servlet.ServletException: Error instantiating servlet class [servlet.HealthCheck]
   (中略)
根本原因:
 java.lang.UnsupportedClassVersionError: servlet/HealthCheck has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0 (unable to load class [servlet.HealthCheck])
   (中略)
注意: 原因のすべてのスタックトレースは、サーバのログに記録されています

Apache Tomcat/9.0.34

調査

Javaのバージョン差異によるもの?

この「UnsupportedClassVersionError」は、

Java仮想マシンが、クラス・ファイルの読込み中に、そのファイルのメジャー・バージョン番号と
マイナー・バージョン番号がサポートされていないと判定した場合にスローされます。

というもの。

つまり、ビルド環境と実行環境のJavaのバージョン差異が原因と考えられる。

冒頭の「環境」に記載した通りビルド環境のjavacのバージョンはjavac 1.8.0_231
実行環境のjavaのバージョンはopenjdk version "1.8.0_252-debug"
となっているため、問題無さそう・・・

コンパイルされたclassファイルのバージョンを調べてみる

[root@ip-10-0-0-50 webapps]# javap -v ./example/WEB-INF/classes/servlet/HealthCheck.class
Classfile /opt/apache-tomcat-9.0.34/webapps/example/WEB-INF/classes/servlet/HealthCheck.class
  Last modified Jun 4, 2020; size 1988 bytes
  MD5 checksum 83f908e6070439cc6281b73baae5306d
  Compiled from "HealthCheck.java"
public class servlet.HealthCheck extends javax.servlet.http.HttpServlet
  minor version: 0
  major version: 55                                   ←★
  flags: ACC_PUBLIC, ACC_SUPER

メジャーバージョンが" 55 "になってる!!

一方で、実行環境のJREバージョンは1.8。こちらのサイト様によると、
JREバージョン1.8がサポートするクラスファイルバージョンは"52"とのこと。

エラーの内容が把握できました。

で、冒頭の「ビルド環境」のところで確認したJavacのバージョンは、Windows全体として確認した
(スタートメニューから開いたcmdで確認した)結果であって、Eclipse上でプロジェクトの
右クリックメニューに出てくる「コマンドプロンプト」で確認できる内容とは異なることが分かった。

>javac -version
javac 11.0.5

D:\31_Pleiades\workspace\example>java -version
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.5+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.5+10, mixed mode)

これを見ると、javacのバージョンは"11.0.5"となっている。
今使っているEclipseは「Pleiades All in One Eclipse」であり、このプラグインの中には複数バージョンのJDKが入っている。

Windowsには別口で導入したJDK1.8が存在し、パスはそちらに通っているため冒頭の確認では気づけなかった。

Eclipseでビルドする際に使用されるjavacは"11.0.5"だったために、今回の問題が発生したというわけ。
ビルドする際に使用するjavacのバージョンを"1.8"に下げることができれば今回の問題はクリアできそう。

対処

ビルド時のJDKバージョンを下げてみる

まずはEclipse全体の設定を確認

プロジェクト固有設定としてバージョンを下げてみる

リビルドしてクラスファイルのバージョンを確認する

>javap -v HealthCheck.class
Classfile /D:/31_Pleiades/workspace/example/build/classes/servlet/HealthCheck.class
  Last modified 2020/06/04; size 1988 bytes
  MD5 checksum 5deb7a7fe4b855e66be9d06261116bd6
  Compiled from "HealthCheck.java"
public class servlet.HealthCheck extends javax.servlet.http.HttpServlet
  minor version: 0
  major version: 52                       ←★成功
  flags: ACC_PUBLIC, ACC_SUPER

改めて.warファイルにエクスポート→サーバ配置→アクセスしてみる

・・・
・・・
・・・

成功!!

というわけで、Javaのバージョンはしっかり確認しましょう!というお話でした。。。