LoginSignup
1
1

ServletのWebアプリをJakarta対応する話(1)

Posted at

Java の Servletで書かれれたWebアプリをメンテしています。
30画面ほどあります。個人開発(自分1人で開発)の社内用Webアプリです。
これをなるべく、手間をかけずに最新のTomcatやWildflyに対応させる話です。

旧アプリ(現状)

・Tomcat 9 と Java11で動いています。
・Servlet APIで書かれています。 2000年頃のサーブレットの書き方です。
・ 最近のサーブレットの書き方(アノテーション)は使用していません。
・Servletはweb.xmlに1個ずつ登録しています。 servletタグと servlet-mappingタグを使って、登録しています。
・表示は JSP のみ。タグリブも使っていない。
・DBアクセスにはMyBatisを使っています。
・ロギングは slf4j-api, logback-core, logback-classicを使っています。
・IDEはNetBeansを使い、antでビルドして、Tomcatに入れています。
・開発はWindowsPC or Macで行い、AWS上のLinuxにデプロイします。
・なおこのAWSのサーバーの前には、KeyCloakとリバースプロキシを置いて認証はそちらに任せています。

なんでこんな技術なのか:
言い訳のようになりますが、重厚長大なフレームワークは個人開発では厳しいのです。問題が発生した際に聞く人がいないからです。中小企業の社内開発なので、仕方ないです。

書き換え後(書き換え中なので希望)

・Tomcat 10または最新のJakartaEEで動かしたい
・既存のソースの書き替えはなるべく少なくしたい
・当面 Java11のまま。半年後をめどに Java17に移行したい
・ビルドツールは ant から mavenに変更 (mavenは苦手なのですが、JakartaEE対応するには、mavenは必須かとあきらめました。

候補となるアプリサーバー

・Tomcat 10は候補として残しておく。現状Tomcat 9なので安心。
・JakartaEEのサーバーは多数ありますが、認証にKeyCloakを使っている事から、そのアプリサーバーは、勉強してもいいなかーと思いました。QuarkusとWildflyです。
・それ以外のアプリサーバーもいくつかインストールだけしてみたのですが、特に気に入ったものはありませんでした。
・Quarkusは、少しアプリも作って見ました。非常に面白かったのですが、上記に書いてある既存のServletアプリを移植するには難しそうな印象を持ちました。アノテーションを使った方法などに、書き換えていけば動くのかも知れませんが、工数がどれくらいかかるか見通せません。
・最終的に Tomcat 10 か WildFly 28.0.1 , 27.0.1 あたりにしたいと思いました。
・WildFlyで動く事が確認できれば、機能が多いので、それにするつもりです。(Tomcat 10は保険)

IDEはどうするか?

・検索した所、eclipse と wildflyで開発している記事をいくつか見つけました。
  それもトライしてみたのですが、最新のWildFlyではうまく動きませんでした。
・eclipseプラグインは WildFly 24あたりでないと対応していない様子でした。
・eclipseと、プラグインで開発するのはあきらめました。

Wildflyのインストールとサンプル

wildfly.png

ダウンロードして、展開するだけなので、特に難しい事はありませんでした。

既存ソースの書き換え

import文の javax.servletを jakarta.servletに書き換えるだけです。
30画面ほどあるので、3時間ほどかかりましたが、単純作業でした。

mavenのpomをどうするか

Tomcat 10用のpom.xmlも検索すればあったのですが、結局 Wildflyのサンプルを参考にして作る事にしました。

helloworldか、 bmtあたりを参考にして作りました

pom.xml
<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.foobar</groupId>
  <artifactId>skill-app</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>skill-app Maven Webapp</name>
  <!-- FIXME change it to the project&amp;apos;s website -->
  <url>http://www.example.com</url>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- The versions for BOMs, Dependencies and Plugins -->
    <version.server.bom>28.0.1.Final</version.server.bom>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <profiles>
    <profile>
      <id>windows</id>
      <build>
        <resources>
          <resource>
            <directory>src/windows/resources</directory>
          </resource>
          <resource>
            <directory>src/common/resources</directory>
          </resource>
        </resources>
      </build>
    </profile>
    <profile>
      <id>mac</id>
      <build>
        <resources>
          <resource>
            <directory>src/mac/resources</directory>
          </resource>
          <resource>
            <directory>src/common/resources</directory>
          </resource>
        </resources>
      </build>
    </profile>
  </profiles>

  <repositories>
    <repository>
      <id>jboss-public-maven-repository</id>
      <name>JBoss Public Maven Repository</name>
      <url>https://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </snapshots>
      <layout>default</layout>
    </repository>
    <repository>
      <id>redhat-ga-maven-repository</id>
      <name>Red Hat GA Maven Repository</name>
      <url>https://maven.repository.redhat.com/ga/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </snapshots>
      <layout>default</layout>
    </repository>
  </repositories>
  <pluginRepositories>
    <pluginRepository>
      <id>jboss-public-maven-repository</id>
      <name>JBoss Public Maven Repository</name>
      <url>https://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
    <pluginRepository>
      <id>redhat-ga-maven-repository</id>
      <name>Red Hat GA Maven Repository</name>
      <url>https://maven.repository.redhat.com/ga/</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </pluginRepository>
  </pluginRepositories>
  <dependencyManagement>
    <dependencies>
      <!-- importing the jakartaee8-with-tools BOM adds specs and other useful artifacts as managed dependencies -->
      <dependency>
        <groupId>org.wildfly.bom</groupId>
        <artifactId>wildfly-ee-with-tools</artifactId>
        <version>${version.server.bom}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>


  <dependencies>
    <!-- Import the CDI API, we use provided scope as the API is included in JBoss EAP -->
    <dependency>
      <groupId>jakarta.enterprise</groupId>
      <artifactId>jakarta.enterprise.cdi-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Import the Common Annotations API (JSR-250), we use provided scope  as the API is included in JBoss EAP -->
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Import the EJB API, we use provided scope as the API is included in JBoss EAP -->
    <dependency>
      <groupId>jakarta.ejb</groupId>
      <artifactId>jakarta.ejb-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Import the Servlet API, we use provided scope as the API is included in JBoss EAP -->
    <dependency>
      <groupId>jakarta.servlet</groupId>
      <artifactId>jakarta.servlet-api</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.13</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.10.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>2.0.4</version>
    </dependency>
  </dependencies>
  <!--
  mvn compile war:exploded -P windows
  
  -->
  <build>
    <finalName>skill</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.2</version>
        <configuration>
          <!-- webappDirectory&amp;gt;C:/usr2/test/skill.war&lt;/webappDirectory -->
          <webappDirectory>C:/usr2/wildfly-28.0.1.Final/wildfly-28.0.1.Final/standalone/deployments/skill.war</webappDirectory>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

mvn clean
mvn package -P windows

target に skill.war が作成されます。(skillというのは、作成しているアプリ名です)

profiles の部分は、WindowsとMacを切り替えるために使っているだけです。
不要なら削除してください

WildFlyに入れてみる

WildFlyのインストールDIR/standalone/deploymentsに上記のwarを入れ
WildFlyのインストールDIR/bin/standalone.batで起動すると
それなりに動作しました。

logback-core, logback-classicは WildFlyでは使えないそうなので、
上記のpom.xmlからは削除しました。
WildFlyのログに混ざって出力されるそうです。slf4j-apiは、入れておく必要があります。

Tomcat10 に入れてみる

Tomcat10のインストールDIR/webappsに上記のwarを入れて、startup.batで起動するとそれなりに動きました。

logback-core, logback-classicは Tomcat 10には入れる必要があります。
maven初心者でやり方が、わからないため、antでこの処理を行っています。

修正箇所(現時点でのまとめ)

・上述の javax.servletを jakarta.servletに変更する
・上述の logback-core, logback-classicの問題
・WEB-INF ディレクトリに置いたプロパティファイルが、なぜか、mac + wildflyの時だけ、うまく動きませんでした。

sample.java
private Properties getConfigProperties(HttpServlet servlet) throws IOException {

        Properties menuProp = new Properties();
        try (InputStream is = servlet.getServletContext().getResourceAsStream("WEB-INF/sample.properties")) {
            
            menuProp.load(new InputStreamReader(is, "UTF-8"));

        }
        return menuProp;
}

servlet.getServletContext().getResourceAsStream()がうまく、動かない様子です。
理由はわかりません。下のように書き替えました。

sample.java (修正)
Properties configProp = new Properties();

InputStream is = I18n.class.getResourceAsStream(path);
if (is == null) {
      LOG.error("prop file not found. {}", path   );
} else {
      try {
          configProp.load(new InputStreamReader(is, "UTF-8"));
      } finally {
          is.close();
      }
}

web.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>MenuServlet</servlet-name>
  <servlet-class>zz.skill.serv.MenuServlet</servlet-class>
</servlet>

途中省略

<servlet-mapping>
  <servlet-name>MenuServlet</servlet-name>
  <url-pattern>/gu/Menu</url-pattern>
</servlet-mapping>

途中省略

<filter>
    <filter-name>oidcEmu</filter-name>
    <filter-class>zz.filter.OidcDevFilter</filter-class>
</filter>

<filter-mapping> 
    <filter-name>oidcEmu</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping> 

</web-app>

先頭の DOCTYPE宣言は最新のJakartaEEだと変わるそうですが、 Tomcat 9時代に使っていたものを、そのまま変更なしで、動きました。

WildFlyのHotデプロイについて

以下の記事を参考にさせてもらいました。(参考1)

インストールディレクトリ/standalone/configration/ 以下にある、以下のファイルを編集します。
赤枠部分を追加しました。

hotdeploy.png

デプロイについては、以下の記事を参考にさせてもらいました。(参考2)

一度、deploymentsディレクトリに warファイルを置いて standalone.batで起動すると
XXX.war.deployedファイルというのが出来ます。

deployed.png

上手の画面キャプチャが合っていなくて、申し訳ないのですが、前述の pom.xmlで作成した
skill.war を置いて、起動した際に、 skill.war.deployedというファイルが出来ます。

この状態で、一度 WildFly を停止します。 Cntrol-Cで、停止。

その状態で、skill.warを削除します。 skill.war.deployedは残しておきます。
そして、エクスプローラーで、 skill.warというフォルダーを作成します。

(参考2)の記事にある
展開形式
zipを展開したWEBアプリケーションです。展開したディレクトリを「xxx.war」のような名前にしたものです。
の事です。

そして、このディレクトリに、mavenの出力を出力できればいいのですが・・

mavenのマニュアルにその方法がありました(参考3)

maven.png

webappDirectoryのタグの所に前述で作成した skill.warというフォルダを指定します。

mvn clean
mvn compile war:exploded -P windows

で、その位置に、warが展開された形で、コンパイルされる事を確認できました。

Hotデプロイを使ってみたところ

DOS窓を2つ立ち上げます。
1つは、WildFlyのディレクトリ/bin でstandalone.batを起動。
もう1つはソースがあるディレクトリの pom.xmlがある位置で開く。

JavaやJSPの編集は、お好きな IDEやエディタで編集。

編集が終わったら、以下を実行。

mvn compile war:exploded -P windows

WildFlyの設定ファイルには、5秒おきに、変更を監視とありますので、5秒ほど経過すると、変更を検知して自動的に読み込んでくれました。

但し、何回か実行を繰り返してみたところ、たまに、mavenが失敗する事がありました。
どうもWildFlyが変更を検知して読み込んでいる途中では、ファイルにロックがかかる様子です。
mavenがそのファイルは別のプロセスで使っているので、書き込めませんというようなエラーが出てしまいました。

とりあえず、開発が出来そうな所までは、行きました。
しばらくWildFlyで遊んでみて、うまく行きそうなら、それにします。
ダメならTomcat 10にするというつもりです。

以上。 最後まで読んでいただき、ありがとうございました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1