WildFly 運用のための覚え書き

もうあまり使うことはないかもしれないが、Java のアプリケーションサーバ WildFly の運用のための覚え書き。

同一物理マシンに複数の WildFly を設置するのは可能か?

レガシーなプロジェクトで、JavaEE ウェブアプリと JakartaEE ウェブアプリが混在するような場合、少なくとも2台のWildFly が必要になる。27 以降の WildFly と 27 より前のバージョンがそれぞれ少なくとも一つは必要。

結論から言うとできる。

ただ、ポートがかち合わないように設定ファイルを変える。
ワイの場合は、各ポート番号に 10000 を足す。

standalone-full.xml
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
        <socket-binding name="ajp" port="${jboss.ajp.port:18009}"/>
        <socket-binding name="http" port="${jboss.http.port:18080}"/>
        <socket-binding name="https" port="${jboss.https.port:18443}"/>
        <socket-binding name="iiop" interface="unsecure" port="13528"/>
        <socket-binding name="iiop-ssl" interface="unsecure" port="13529"/>
        <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:19990}"/>
        <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:19993}"/>
        <socket-binding name="txn-recovery-environment" port="14712"/>
        <socket-binding name="txn-status-manager" port="14713"/>
        <outbound-socket-binding name="mail-smtp">
           <remote-destination host="${jboss.mail.server.host:localhost}" port="${jboss.mail.server.port:10025}"/>
        </outbound-socket-binding>
        <outbound-socket-binding name="messaging-activemq">
            <remote-destination host="${jboss.messaging.connector.host:localhost}" port="${jboss.messaging.connector.port:51616}"/>
        </outbound-socket-binding>
    </socket-binding-group>

ただし、このやり方だと activemq は 71616 となり

A maximum value of 65535 is required

と WildFly に怒られるので、10000 を引く。

こうしておくと以下のように管理画面も同時に表示できる。

めでたしめでたし。

WildFly をサービス化する

JBOSS_HOME の docs 以下のフォルダに各種サンプルファイルがあるので、これをうまく活用する。
Ubuntu の場合は、以下のようにそのまま使える。

sudo mkdir /etc/wildfly
sudo cp /opt/wildfly/docs/contrib/scripts/systemd/wildfly.conf /etc/wildfly
sudo cp /opt/wildfly/docs/contrib/scripts/systemd/wildfly.service /etc/systemd/system/wildfly.service
sudo cp /opt/wildfly/docs/contrib/scripts/systemd/launch.sh /opt/wildfly/bin/launch.sh

この作業が終わった後、初回起動は

sudo systemctl enable --now wildfly

というコマンドで。

jboss-cli.sh による deploy

なお、サービスが走っている場合のデプロイは jboss-cli.sh を走らせ、そこから

deploy PathTo/hoge.war

で投入できる。

バックグランドで起動させる

Java の慣習でしょうか & を付ける。

standalone.sh -c stanalone-full.xml &

Ctrl+C でメッセージを終了させてもプロセスは終わってない模様。

コマンドラインから停止

バックグランドで走らせた状態では Ctrl+C では停止できないので以下のコマンドを使う。

jboss-cli.sh --connect command=shutdown

 

(適宜修正予定)

JAX-RS と JSON と List と hibernate

おさらい

REST api を自作してると response として List を含んだ JSON を返したいということがよくあるが、簡単なサンプルがなかったりするので、簡単なやつを作ってみた。


@Path("/persons")
public class SimpleResource {
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> list() {
        
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("秋葉");
        person2.setName("akiba");
        List<Person> persons = new ArrayList<>();
        persons.add(person1);
        persons.add(person2);
        
        return persons;
        
    }
    
    private class Person{
        
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
    
}

これをアプリケーションサーバにデプロイしてブラウザで http://…/persons にアクセスすると

[{"name":"秋葉"},{"name":"akiba"}]

という文字列が表示される。

だから何?って言われそうだが、やってないとけっこう忘れますw

返値の型は List<Person> なので、JSON で配列を表す [] が先にきて、インスタンスが二つあるので {} が二つ続くと。

 

この程度なら単純なのだが、ややこしくなってくるのは、Person クラス自体にコレクションを含むような場合だ。

ちょっとやってみよう。


@Path("/persons")
public class SimpleResource {
 
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> list() {
        
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("秋葉");
        person2.setName("akiba");
        person1.addfriend("上野");person1.addfriend("新橋");person1.addfriend("神田");
        List<Person> persons = new ArrayList<>();
        persons.add(person1);
        persons.add(person2);
        
        return persons;
        
    }
    
    private class Person{
        
        private String name;
     List<String> friends = new ArrayList<>();

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void addfriend(String name){
            friends.add(name);
        }

        public List getFriends() {
            return friends;
        }

        public void setFriends(List friends) {
            this.friends = friends;
        } 

    }
    
}

Person クラスに String の List friends を追加した。

この場合のブラウザ出力は

[{"name":"秋葉","friends":["上野","新橋","神田"]},{"name":"akiba","friends":[]}]

となる。

本題 (インスタンスの List を hibernate で操作 )

実用的なシステムを組む場合、上の friends 相当箇所が別のクラス(エンティティ)になる。

Friend というクラスがあって、List<Friend> friends を定義する、みたいな。

この場合、hibernate を組み込んで、@OneToMany だののアノテーションをつけることになると思うが、巷の教科書的な解説はアノテーションの付け方を変に複雑にしているせいで、わかりにくくなっている。

次の記事でシンプルなサンプルを提示する予定。

 

ものぐさ Jersey

Java の EE 環境は JakartaEE に移行しつつあるが、当然、EE の各仕様もアップデートされている。
仕様すべての特徴を理解するのは面倒だが、使う機能は限られている。
おそらく JAX-RS や JPA, CDI あたりが基本で、それらの仕様の概略が頭に入ってれば、実務的にはそうは困らないと思う。

で、JAX-RS の話。

JAX-RS は仕様なので、実際に使う際にはこれを参照した実装が必要。

実際のアプリケーションサーバーでは

WildFly・・・RestEasy

GlassFish, Payara・・・Jersey

がモジュールとして組み込まれている。

前回、Payara を取り上げたので、今回は Jersey。

現在(2022冬)の最新版は 3.1.0 だそうで、そのユーザー向けドキュメントがここにある。

気がついたことなどをいくつか。

クライアント

クライアントでの使い方。

リクエストを投げる時は Invocation で

Client client = ClientBuilder.newClient(); //client の作成

だとか

WebTarget target = client.target(uri).path(path); //target の作成

あたりは ReastEasy とさほど違わないが、実際にサーバーにリクエストを投げる時は

Invocation.Builder builder = target.request(); //リクエスト

と builder を作成してから

buider.get(); // GET メソッドの場合
buider.post(Entity.entity({json のデータ}, MediaType.APPLICATION_JSON)); // POST メソッドの場合

などとするらしい。

また、レスポンスを取り出すときは

Response response = builder.get(); //レスポンス

とするらしい。

これは知らないと使えない。

レスポンスから中身を取り出す

レスポンスから中身を取り出したいときは、多分、色々なやり方があると思うが、いきなオブジェクトマッパーを使ったりすると初心者は混乱すると思うので、まずは String で取り出すのがいいと思う。

result (String です) = response.getEntity(String.class); //なんと1行

と実に簡潔に実現できる。
あるいは、状況によっては

result (String です) = response.readEntity(String.class); //なんと1行

となる。

プロジェクト作成初期なぞ、サーバーと通信できているかどうか不安になると思うので、まずは、こういったコードで確認するのがいいと思う。
まあ、日本語は文字化けはすると思いますが。。。

JSON とか Jackson とか

一歩進む。

JSON を使う

この記事が参考になるかな。

pom.xml に何か加えよ、とあるが、これおそらく Jersey を GlassFish あたりで使う場合。
WildFly(27) だとこの指定は全く不要。
同様のコードをサーバープログラムに書いて、WildFly で稼働させ、 curl でアクセスしたら以下のように普通に動いた。

まあ、ただ、Response で返す、というのは、知識の定着によき。

Jackson JSON を使う

Jersey 公式がサンプルを提示してくれている。

しかし、これわかりにくいなあ。

Jersey や RestEasy を使うのは、特定のクラスのインスタンスを(JSON 形式で)クライアント・サーバー間でやり取りしたいってときなので、ドンピシャのサンプルという感じがしない。

RestEasy はもっと簡単だったような気がするが、はて?

→ やはり、典型的なオブジェクトをやり取りするだけだったら、変に複雑にする必要はなさそうです。

例えば、サーバーサイドから User.class の JSON 形式のオブジェクト

{"id":"123","name":"akiba taro"}

を返し、クライアントで受け取るなら、単に

user = response.readEntity(User.class);

とすればいいだけです。
もちろん、サーバー側では User で返す必要がありますが。

ところで Jackson とは?

Java 用の JSON パーサーライブラリの1つ。Java オブジェクトと JSON の相互変換とかができる。

みたいに説明されている。 まあ、そんなところでしょう。

 

 

Tomcat/Tomee の WAR ファイルのアップロードのサイズ上限

これまで動作確認程度の小さな war を GUI マネージャー経由でデプロイすることがもっぱらだったんだが、もうちょっとエンティティ増やして・・とかやっているうちにファイルが大きくなり、デプロイ時に OK は出るものの管理画面に反映されないようになった。

ログ見たら、ファイルサイズの上限超えていてデプロイできないとかなんとか。

上限解放は

/webapps/manager/WEB-INF/web.xml

<multipart-config>
<max-file-size>52428800</max-file-size>
<max-request-size>52428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

を適当な数値に書き換える。

<multipart-config>
<!– 100MB max –>
<max-file-size>104857600</max-file-size>
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

にしておけば、100 MB 程度まではアップできる。

ただ、これ面白いのは、この数値はあくまで GUI マネージャーを介してのファイルサイズ上限で、同じやつを埋め込みで起動していた時には、まったく問題なく動いていた。

 

TomEE 使ってみての雑感

いくつかのサンプル作成してみての雑感などをあれこれ。

Tomcat に JavaEE(JakartaEE) の機能が追加された、というのを額面通りに受け取ると、あたかも TomEE は Tomcat の上位互換のような印象を受けるが、ちょっと違うかな?と思わないでもない。

埋め込みで使うのが苦手かもしれない(裏技的解決法あり)

今、ちょっと取り組んでいるのが embedded (埋め込み)で使う場合。
tomcat では方法論が確立されているが、tomee ではサンプルすら見つけられない。

それっぽいライブラリはあるのでできなくはないと思うんだが?

ただ、以下のような点は気になる。

少々古い記事になるが、RedHat の中の人が、こんな記事を書いていた。

In summary, of course Spring Boot is cool, but with really simple steps you can start having the same in JavaEE world. We (Apache TomEE contributors) are going to start to work on this and expand this idea.

So don’t underestimate Java EE because of Spring Boot.

 

underestimate (過小評価)しないでくれ、とは言っているが、そこで挙げられたソースコードはあまり実用的とはいえないシロモノで、それで信用しろと言われてもねえ。。。

Spring Boot との比較をしているが、tomcat でできることができないとなると、もはや戦っている土俵が違うという感じだ。

なお、tomcat を埋め込みで使い、さらに heroku にデプロイした例が heroku のサンプルにある。

実は、これを参考にして試行錯誤(まあ、今回も周囲の方々に訊きまくりましたが…orz)していたら、できてしまった。

Tomee を埋め込みで使う場合の裏技?

tomee 本家提供の maven plugin を使うのではなく、通常の maven plugin を使えばよかったのですね。

後で問題は出てくるのかもしれないが、とりあえずは動いた。

なお、railway でも同様のやり方でデプロイ可能。

煮詰まった時の打開策は、経験ある人とちょっとまだ差があるなあ(嘆息)。

同じ Java でしょ?

しかし、コミュニティの運営方針に若干違和感を感じた。

今回の件もそうだが、tomee 純正というか独自仕様に拘り過ぎという気がしないでもない。

通常 maven plugin が EE の仕様に従っていれば、そりゃ tomee でも動く(はず)。

なぜ、それを先に言わない???

そりゃ tomee 用の Application Runner があればそれに越したことはないが、現状だと all features are not yet supported だそうなので、本格的に採用するわけにいかない。

だから、「それが完成するまでは、伝統的な maven plugin を使っていてね」でいいんじゃない?

それを明言しないものだから、取っ付き辛さ→使用者がそれほどいない、という事態の一因になっている気がする。