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のインストールとサンプル
ダウンロードして、展開するだけなので、特に難しい事はありませんでした。
既存ソースの書き換え
import文の javax.servletを jakarta.servletに書き換えるだけです。
30画面ほどあるので、3時間ほどかかりましたが、単純作業でした。
mavenのpomをどうするか
Tomcat 10用のpom.xmlも検索すればあったのですが、結局 Wildflyのサンプルを参考にして作る事にしました。
helloworldか、 bmtあたりを参考にして作りました
<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&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&gt;C:/usr2/test/skill.war</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の時だけ、うまく動きませんでした。
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()がうまく、動かない様子です。
理由はわかりません。下のように書き替えました。
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について
<!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/ 以下にある、以下のファイルを編集します。
赤枠部分を追加しました。
デプロイについては、以下の記事を参考にさせてもらいました。(参考2)
一度、deploymentsディレクトリに warファイルを置いて standalone.batで起動すると
XXX.war.deployedファイルというのが出来ます。
上手の画面キャプチャが合っていなくて、申し訳ないのですが、前述の 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)
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にするというつもりです。
以上。 最後まで読んでいただき、ありがとうございました。