Jboss EAP 7系でjava.lang.NoClassDefFoundErrorが出て躓いた話

# 前置き

解決に、思った以上に時間が掛かってしまったので
今後に皆さんも困らないように情報まとめておきます。

# 環境構成
* Jboss EAP 7.3
* OpenJDK 15

# 作業経緯

Jboss EAPをクリーンインストールのうえ、[ガイドに則ってインフラまわり設定](https://access.redhat.com/documentation/ja-jp/red_hat_jboss_enterprise_application_platform/7.3/html/configuration_guide/index)した際に
正しく導入できたか？確認するべく
シンプル構成のWebアプリを作りたくなった。

# ソースコード ＊不具合を埋め込んだVer

間違いを探しながら読んでください。。

```xml:web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/Hello</url-pattern>
  </servlet-mapping>
</web-app>
```

```java:HelloServlet.java
import java.io.IOException;
import java.io.PrintWriter;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

/**
 * 参考:Jakarta EE公式サンプル
 * https://github.com/eclipse-ee4j/jakartaee-examples/blob/master/servlet/webServlet/src/main/java/jakartaee/examples/servlet/webservlet/WebServletServlet.java
*/
@WebServlet("/HelloWorld")
public class HelloServlet extends HttpServlet{

    static String PAGE_HEADER = "<html><head><title>helloworld</title></head><body>";
    static String PAGE_FOOTER = "</body></html>";

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");

        PrintWriter out = resp.getWriter();

        out.println(PAGE_HEADER);
        out.println("Hello WebServlet world!");
        out.println(PAGE_FOOTER);
        out.close();

    }
}
```

```xml:pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>demo Maven Webapp</name>
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-web-api</artifactId>
      <version>9.0.0</version>
      <scope>provided</scope>
    </dependency>
   <dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>9.0.0</version>
    <scope>provided</scope>
   </dependency>
  </dependencies>

  <build>
    <finalName>demo</finalName>
    <pluginManagement>
      <plugins>
       <!-- 割愛 -->
      </plugins>
    </pluginManagement>
  </build>
</project>
```

# パッケージング

mvnコマンドでwar作ります。

```powershell
mvn clean package install -f "[pom格納したフォルダ]demo\pom.xml"
```

# デプロイ ⇒ 失敗

warを、Jboss管理コンソールや、CLI管理ツール経由でデプロイします。

その結果、以下のとおり**デプロイ失敗**します。

`java.lang.NoClassDefFoundError`が出ますね。
特に難しいこと考えずに`HttpServlet`を使っただけなのですが。。

```text:server.log
WARN  [org.jboss.as.ee] (MSC service thread 1-2) WFLYEE0007: 例外が原因でオプションのコンポーネント servlettest.HelloWorld をインストールしていません (DEBUG ログレベルを有効にして原因を確認してください)
WARN  [org.jboss.modules.define] (MSC service thread 1-2) Failed to define class servlettest.HelloWorld in Module "deployment.demo-1.0-SNAPSHOT.war" from Service Module Loader: java.lang.NoClassDefFoundError: Failed to link servlettest/HelloWorld (Module "deployment.demo-1.0-SNAPSHOT.war" from Service Module Loader): jakarta/servlet/http/HttpServlet
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1095)
        at org.jboss.modules.ModuleClassLoader.doDefineOrLoadClass(ModuleClassLoader.java:424)
        at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:555)
        at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:339)
        at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:126)
        at org.jboss.modules.Module.loadModuleClass(Module.java:731)
        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:247)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
        at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
        at org.wildfly.extension.undertow@7.3.0.GA-redhat-00004//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.createServletConfig(UndertowDeploymentInfoService.java:718)
        at org.wildfly.extension.undertow@7.3.0.GA-redhat-00004//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.start(UndertowDeploymentInfoService.java:275)      
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:832)

ERROR [org.jboss.msc.service.fail] (MSC service thread 1-2) MSC000001: Failed to start service jboss.deployment.unit."demo-1.0-SNAPSHOT.war".undertow-deployment.UndertowDeploymentInfoService: org.jboss.msc.service.StartException in service jboss.deployment.unit."demo-1.0-SNAPSHOT.war".undertow-deployment.UndertowDeploymentInfoService: Failed to start service   
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1731)
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
        at org.jboss.threads@2.3.3.Final-redhat-00001//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1377)
        at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.NoClassDefFoundError: Failed to link servlettest/HelloWorld (Module "deployment.demo-1.0-SNAPSHOT.war" from Service Module Loader): jakarta/servlet/http/HttpServlet   
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1095)
        at org.jboss.modules.ModuleClassLoader.doDefineOrLoadClass(ModuleClassLoader.java:424)
        at org.jboss.modules.ModuleClassLoader.defineClass(ModuleClassLoader.java:555)
        at org.jboss.modules.ModuleClassLoader.loadClassLocal(ModuleClassLoader.java:339)
        at org.jboss.modules.ModuleClassLoader$1.loadClassLocal(ModuleClassLoader.java:126)
        at org.jboss.modules.Module.loadModuleClass(Module.java:731)
        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:247)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
        at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
        at org.wildfly.extension.undertow@7.3.0.GA-redhat-00004//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.createServletConfig(UndertowDeploymentInfoService.java:718)
        at org.wildfly.extension.undertow@7.3.0.GA-redhat-00004//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.start(UndertowDeploymentInfoService.java:275)      
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
        at org.jboss.msc@1.4.11.Final-redhat-00001//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
        ... 6 more

ERROR [org.jboss.as.controller.management-operation] (External Management Request Threads -- 1) WFLYCTL0013: 操作 ("add") の失敗 - アドレス: ([("deployment" => "demo-1.0-SNAPSHOT.war")]) - 問題の詳細: {"WFLYCTL0080: サービスに問題がありました" => {"jboss.deployment.unit.\"demo-1.0-SNAPSHOT.war\".undertow-deployment.UndertowDeploymentInfoService" => "Failed to start service
    Caused by: java.lang.NoClassDefFoundError: Failed to link servlettest/HelloWorld (Module \"deployment.demo-1.0-SNAPSHOT.war\" from Service Module Loader): jakarta/servlet/http/HttpServlet"}}
ERROR [org.jboss.as.server] (External Management Request Threads -- 1) WFLYSRV0021: 失敗メッセージ
{"WFLYCTL0080: サービスに問題がありました" => {"jboss.deployment.unit.\"demo-1.0-SNAPSHOT.war\".undertow-deployment.UndertowDeploymentInfoService" => "Failed to start service
    Caused by: java.lang.NoClassDefFoundError: Failed to link servlettest/HelloWorld (Module \"deployment.demo-1.0-SNAPSHOT.war\" from Service Module Loader): jakarta/servlet/http/HttpServlet"}} とともに、デプロイメント "demo-1.0-SNAPSHOT.war" のデプロイはロールバックされました。
INFO  [org.jboss.as.server.deployment] (MSC service thread 1-2) WFLYSRV0028: 5ms でデプロイメント demo-1.0-SNAPSHOT.war (runtime-name: demo-1.0-SNAPSHOT.war) を停止しました。 
INFO  [org.jboss.as.repository] (ServerService Thread Pool -- 40) WFLYDR0009: コンテンツ [省略] は廃止されたため削除されます
INFO  [org.jboss.as.repository] (ServerService Thread Pool -- 40) WFLYDR0002: ロケーション [省略] からコンテンツを削除

```

# 何がダメだったか？

<details><summary>答え合わせセクションです。クリックすると開きます。</summary><div>

* 環境構成に示したJboss EAP 7.3は、Jakarta EE 8.0対応。Jakarta EE 9.0は、非対応です。

> JBoss Enterprise Application Platform でサポートされる標準 
> https://access.redhat.com/ja/articles/299293

* `javax`から`jakarta`にパッケージ変更されたのはJakarta EE 9.0からです。


* つまりソースコードは以下が誤りです。
  * 念のためですが、同一Servlet(`HelloServlet`)を
    `/HelloWorld`と`/Hello`の2URLにマッピングした点は、特に問題ないです。


```diff_java:HelloServlet.java
- import jakarta.servlet.ServletException;
- import jakarta.servlet.annotation.WebServlet;
- import jakarta.servlet.http.HttpServlet;
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
+ import javax.servlet.ServletException;
+ import javax.servlet.annotation.WebServlet;
+ import javax.servlet.http.HttpServlet;
+ import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.http.HttpServletResponse;
```

```diff_xml:pom.xml
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-web-api</artifactId>
-      <version>9.0.0</version>
+      <version>8.0.0</version>
      <scope>provided</scope>
    </dependency>
   <dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
-    <version>9.0.0</version>
+    <version>8.0.0</version>
    <scope>provided</scope>
   </dependency>
```

* 躓き＆学んだポイントは
『Jakarta EE 9のコードとしては一切の問題ないwarを
Jboss EAP 7系、すなわちJakarta EE 9非対応のAPサーバにデプロイすると、NoClassDefFoundErrorが出ますよ』でした。

</div></details>

# 終わりに

基底クラス関連に係る例外発生なので
ついつい「Jboss追加モジュール入れ忘れたかな？」「利用クラス`WebServlet`使い方を間違えたかな？」等と、考えがちですが。。。
NoClassDefFoundError出た際には、まず基本に立ち返るようにしましょう。