タグ「Payara Micro」が付けられているもの

How to use Payara Micro Maven Plugin (ja)

先頃リリースされた Payara 172 はクラウドに焦点を当てた様々な新機能を提供しています。それら新機能の中に Payara Micro Maven Plugin があります。小さな機能ですが、Payara Micro で動作するアプリケーションを開発するのにとても便利です。

従来、Payara Micro 向けのアプリケーションを Payara Server なしで開発するのは難しいことでした。IDE が Payara Micro と連携しないためです。これが Payara Micro Maven Plugin を使用することによって改善されます。

1. Payara Micro Maven Plugin の準備

Payara Micro Maven Plugin の最新バージョンは 1.0.0-SNAPSHOT であり、まだ Maven central リポジトリーに収録されていません。従って、まずはソースコードを GitHub からチェックアウトしてビルドするところから始めます。

$ git clone https://github.com/payara/maven-plugins
$ cd maven-plugins/payara-micro-maven-plugins
$ mvn install

2. Payara Micro Maven Plugin の使い方

まずは Payara Micro Maven Plugin の GAV を示します。

KeyValue
groupId fish.payara.maven.plugins
artifactId payara-micro-maven-plugin
version 1.0.0-SNAPSHOT

Payara Micro Maven Plugin は 3 つのゴール、payara-micro:bundle, payara-micro:start and payara-micro:stop が定義されています。それぞれ以下の通りです。

GoalDescription
payara-micro:bundle Uber Jar を作成する
payara-micro:start アプリケーションを Payara Micro 上で実行する
payara-micro:stop アプリケーション (Payara Micro で実行中) を停止する。

pom.xml に最小限追記すべき事項は以下の通りです。

<build>
  <plugins>
    <!-- snip -->
    <plugin>
      <groupId>fish.payara.maven.plugins</groupId>
      <artifactId>payara-micro-maven-plugin</artifactId>
      <version>1.0.0-SNAPSHOT</version>
    </plugin>
    <!-- snip -->
  </plugins>
</build>

ただし、これにはいくつかの問題があります。

  • payara-micro:bundle の実行前に War ファイルを作成しておかなければなりません。War ファイルが存在しない状況で実行するとエラーになります。
  • payara-micro:start は既定では Uber Jar を実行しません。このゴールは Payara Micro の様々なオプションをサポートするため、Uber Jar には特化していないことによります。
  • 既定では、プラグインは Payara Micro 4.1.1.171 を使用します。ただし、このバージョンは Windows では全く動作しないというバグがあります (そのため公式アナウンス時には既に 4.1.1.171.0.1 となっていたのです)。

では、実質的な最小限の追記事項を以下に示します。

<build>
  <plugins>
    <!-- snip -->
    <plugin>
      <groupId>fish.payara.maven.plugins</groupId>
      <artifactId>payara-micro-maven-plugin</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <executions>
        <execution>
          <goals>
            <goal>bundle</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <payaraVersion>4.1.2.172</payaraVersion>
        <useUberJar>true</useUberJar>
      </configuration>
    </plugin>
    <!-- snip -->
  </plugins>
</build>

ゴール install 実行と併せて payara-micro:bundle も実行されます。そのため、アプリケーションのビルドから Uber Jar の作成までを 1 ステップ、`mvn install` だけで済みます (どうです、簡単でしょう?)。

実行後、おそらく project-name/target 下に 2 つのファイルが出来ていることでしょう。

  • application-name.war (e.g. helloweb-1.1.0.war)
  • application-name-microbundle.jar (e.g. helloweb-1.1.0-microbundle.jar)

2 番目のファイルが Uber Jar です。これを直接 CLI で実行しても、あるいは Maven プラグイン経由 (payara-micro:start および payara-micro:stop) で実行しても構いません。Maven プラグインを使用することで任意の IDE と連携させることが出来ます。そのため、Payara Server の助けを借りなくても Payara Micro 向けのアプリケーションを容易に開発できるようになりました。

3. まとめ

Payara Micro を使うのはとても簡単です。現在ではさらに Payara Micro Maven Plugin の登場により、任意の IDE と連携させることができるようになっています。

詳細については Payara Micro Maven Plugin のページ をご覧ください。

4. おまけ

この記事を書くに当たって、私はサンプルアプリケーションとして https://github.com/khasunuma/helloweb/ を使用しました。これをビルドおよび実行すると、IDE のコンソールに以下のようなログが出力されます。

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building helloweb 1.1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- payara-micro-maven-plugin:1.0.0-SNAPSHOT:start (default-cli) @ helloweb ---
[INFO] Starting payara-micro from path: C:\Users\Kenji\workspace\helloweb\target/helloweb-1.1.0-microbundle.jar
(snip)
[2017-06-15T21:43:17.604+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597604] [levelValue: 800] 
Instance Configuration
Host: lysithea.voyager.coppermine.jp
HTTP Port(s): 8080 
HTTPS Port(s): 
Instance Name: Aggressive-Batfish
Instance Group: MicroShoal
Hazelcast Member UUID 1fb214f2-ac78-454e-a52c-81faf0d4f8b3
Deployed: helloweb-1.1.0 ( helloweb-1.1.0 war /helloweb-1.1.0 )



[2017-06-15T21:43:17.606+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597606] [levelValue: 800] 
Payara Micro URLs
http://lysithea.voyager.coppermine.jp:8080/helloweb-1.1.0

'helloweb-1.1.0' REST Endpoints
GET	/helloweb-1.1.0/api/hello
GET	/helloweb-1.1.0/api/hello



[2017-06-15T21:43:17.606+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597606] [levelValue: 800] Payara Micro  4.1.2.172 #badassmicrofish (build 235) ready in 12,255 (ms)

Payara Micro 172 以降では、アプリケーションの URLs と REST エンドポイントがログの最後に出力されるようになりました。これでもうパスを間違えずに済みますね。

How to use Payara Micro Maven Plugin

Payara 172 has various new features that are highly cloud-focused. A part of them, Payara Micro Maven Plugin is provided. It's a small but useful tools to develop applications running on Payara Micro.

Previously, there is a problem that it's difficult to develop applications for Payara Micro without Payara Server because no IDEs support Payara Micro directly. Now it has solved to use Payara Micro Maven Plugin.

1. Preparing Payara Micro Maven Plugin

Latest version of Payara Micro Maven Plugin is 1.0.0-SNAPSHOT, thus it's not on Maven central repository. At first we check out from GitHub and build it.

$ git clone https://github.com/payara/maven-plugins
$ cd maven-plugins/payara-micro-maven-plugins
$ mvn install

2. Using Payara Micro Maven Plugin

Here is GAV of Payara Micro Maven Plugin.

KeyValue
groupId fish.payara.maven.plugins
artifactId payara-micro-maven-plugin
version 1.0.0-SNAPSHOT

Payara Micro Maven Plugin has three goals; payara-micro:bundle, payara-micro:start and payara-micro:stop.

GoalDescription
payara-micro:bundle Create Uber Jar
payara-micro:start Start the application on Payara Micro
payara-micro:stop Stop the application on Payara Micro

Then minimal description on pom.xml is here:

<build>
  <plugins>
    <!-- snip -->
    <plugin>
      <groupId>fish.payara.maven.plugins</groupId>
      <artifactId>payara-micro-maven-plugin</artifactId>
      <version>1.0.0-SNAPSHOT</version>
    </plugin>
    <!-- snip -->
  </plugins>
</build>

But minimal description has some problems.

  • We have to create a War file before run payara-micro:bundle. The goal assumes the War file exists.
  • Goal payara-micro:start doesn't use Uber Jar in default. The goal supports various option of Payara Micro, thus it's not specified to Uber Jar.
  • In default, this plugin uses Payara Micro 4.1.1.171. Unfortunately, this version cannot run on Windows platform because of its bug. (Therefore Payara Micro 4.1.1.171.0.1 was released instead of it.)

Then, I show effectively minimal description as follows:

<build>
  <plugins>
    <!-- snip -->
    <plugin>
      <groupId>fish.payara.maven.plugins</groupId>
      <artifactId>payara-micro-maven-plugin</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <executions>
        <execution>
          <goals>
            <goal>bundle</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <payaraVersion>4.1.2.172</payaraVersion>
        <useUberJar>true</useUberJar>
      </configuration>
    </plugin>
    <!-- snip -->
  </plugins>
</build>

This runs goal payara-micro:bundle with goal install. So that we may do only one step `mvn install` to build an application and create Uber Jar. (I think it's very useful, don't you?)

After the goal, it seems that there are two files in project-name/target.

  • application-name.war (e.g. helloweb-1.1.0.war)
  • application-name-microbundle.jar (e.g. helloweb-1.1.0-microbundle.jar)

The second is Uber Jar. We may run it either on CLI directly or via the plugin's goals; payara-micro:start and payara-micro:stop. The way to use the plugin can be integrated with any IDEs, so we're able to develop applications for Payara Micro easily even if we don't use Payara Server.

3. Conclusion

To use Payara Micro is very easy. Now Payara Micro is integrated with any IDEs by using Payara Micro Maven Plugin.

If you want to know more information, see Payara Micro Maven Plugin's page.

4. Appendix

To write this article, I use https://github.com/khasunuma/helloweb/ as a sample application. And build and run it using the plugin, it's logging in the console of my IDE as follows:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building helloweb 1.1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- payara-micro-maven-plugin:1.0.0-SNAPSHOT:start (default-cli) @ helloweb ---
[INFO] Starting payara-micro from path: C:\Users\Kenji\workspace\helloweb\target/helloweb-1.1.0-microbundle.jar
(snip)
[2017-06-15T21:43:17.604+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597604] [levelValue: 800] 
Instance Configuration
Host: lysithea.voyager.coppermine.jp
HTTP Port(s): 8080 
HTTPS Port(s): 
Instance Name: Aggressive-Batfish
Instance Group: MicroShoal
Hazelcast Member UUID 1fb214f2-ac78-454e-a52c-81faf0d4f8b3
Deployed: helloweb-1.1.0 ( helloweb-1.1.0 war /helloweb-1.1.0 )



[2017-06-15T21:43:17.606+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597606] [levelValue: 800] 
Payara Micro URLs
http://lysithea.voyager.coppermine.jp:8080/helloweb-1.1.0

'helloweb-1.1.0' REST Endpoints
GET	/helloweb-1.1.0/api/hello
GET	/helloweb-1.1.0/api/hello



[2017-06-15T21:43:17.606+0900] [] [INFO] [] [PayaraMicro] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1497530597606] [levelValue: 800] Payara Micro  4.1.2.172 #badassmicrofish (build 235) ready in 12,255 (ms)

Payara Micro 172 or later, it shows URLs and REST Endpoints in the end of starting log. So we're able to obtain the paths definitely.

Using GAV deploy on Payara Micro

This article is the translation that written in Japanese on 20th December 2016. The original article (Japanese) is here.

We can deploy WAR files on Maven repositories to Payara Micro directly. In this article, I'll show it by a sample. Using GAV deploy needs to prepare a Maven repository at once, but it's easy after preparing it.

1. Prepare: Create a WAR file and register to a Maven repository

At the first, we need to prepare a Maven repository to upload a WAR file. If you can use Maven Central repository, it's the best. (See also http://maven.apache.org/guides/mini/guide-central-repository-upload.html when to upload Maven Central) Otherwise, use Sonatype Nexus for building a Maven repository.

We may upload any WAR files that is able to run on Payara Micro. If you need, multiple WAR files can be deployed also GAV deploy. But using snapshot version is easy to happen a trouble, so I recommend to use release version.

2. How to use GAV deploy

I make a sample WAR file that output "Hello, world" using JAX-RS. The code is on https://github.com/khasunuma/helloweb/tree/version-1.0. It's already upload to a Maven repository I prepared.

Then run to following command line, and deploy and run the sample.

When writing the original articles, the latest version of Payara Micro is 4.1.1.164.

java -jar payara-micro-4.1.1.164.jar --deployFromGAV jp.coppermine.samples:helloweb:1.0.0 --additionalRepository http://repo1.cubiwano.org/repository/maven-public

Give groupId, artifactId and version of the WAR file by --deployFromGAV option. And if use the in-house repository (not Maven Central), give the URL by --additionalRepository option. (In this case, the URL is http://repo1.haswell.jp/repository/maven-public)

Attention: the Maven repository using the sample (repo1.cubiwano.org) is accept both HTTP and HTTPS but should use "HTTP" in GAV deploy.

If you open http://localhost:8080/helloweb/api/hello by a Web browser, it shall show "Hello, world".

Using Payara Micro API is following. (the complete code is on https://github.com/khasunuma/payaramicro-gav)

package jp.copermine.samples.payaramicro;

import fish.payara.micro.BootstrapException;
import fish.payara.micro.PayaraMicro;

public class GavDeploy {

    public static void main(String[] args) throws BootstrapException {
        // Required if not using Maven central repository 
        final String repositoryUrl = "http://repo1.cubiwano.org/repository/maven-public";
        
        // groupId , artifactId , version
        // if version is SNAPSHOT, it may not run well
        final String gav = "jp.coppermine.samples,helloweb,1.0.0";
        
        PayaraMicro.getInstance().addRepoUrl(repositoryUrl).addDeployFromGAV(gav).setNoCluster(true).bootStrap();
    }

}

Payara Quick Start Guide

This article is revised and English transleted of the post 3rd December 2016.

Recently, Payara users are growing near by me. Most of them migrate from GlassFish, but it's certainly that Payara is the first Java EE server for someone. Then I note minimum instruction for using Payara.

0. Requirements

0.1. JDK

Payara is required JDK 7, or JDK 8 Update 20 or later. About JDK's set up is omitted because it's dependent with target OS. But I suggest to use the newest as possible. Remember setting path to JVM (java / java.exe). Attention: Payara ignores JAVA_HOME environment variable.

0.2. Network settings

In case of some OS, almost listen ports are closed in default. If your environment is such a thing, you need to add a settings of listen ports. Payara listens mainly TCP 4848, 8080 and 8181 ports. In addition to enable Hazelcast, you need to open 5900 and later ports (maximum 5999). Note: To use Payara Micro, Hazelcast is enabled in default.

1. Instruction of development environment

1.1. Downloading Payara Server

Download Payara Server from http://www.payara.fish/downloads (If you need Multi-Language edition, Access to http://www.payara.fish/all_downloads). When to download, you don't any registrations. Even if you use Payara Micro as operation, it's easy to develop applications on Payara Server.

In case of Payara 4.1.1.171.1 (latest release at the time), you may select following one;

  • Payara 4.1.1.171.1 (Full) : payara-4.1.1.171.1.zip
  • Payara-Web 4.1.1.171.1 (Web Profile) : payara-web-4.1.1.171.1.zip
  • Payara-ML 4.1.1.171.1 (Multi-Language Full) : payara-ml-4.1.1.171.1.zip
  • Payara-Web-ML 4.1.1.171.1 (Multi-Language Web Profile) : payara-web-ml-4.1.1.171.1.zip

I suggest you may select Full Platform i.e. payara-4.1.1.171.1.zip for development. If you need Web Profile (e.g. planning to use Payara Micro in operation), restricted by Maven dependency (javax:javax-ee-web:7.0).

1.2. Installation of Payara Server

Extract payara-4.1.1.171.1.zip to any directory. But on Windows, avoid to the directory it's named spaces (e.g. C:\Program Files). If you are an administrator of the computer, extract to C:\ (on Windows) or /opt (on Linux/Solaris) for ease. Then Payara Server installed on C:\payara41 (on Windows) or /opt/payara41 (on Linux/Solaris).

If you're not an administrator, it may extract to %USERPROFILE% (C:\Users\username) on Windows or $HOME (/export/home/username or /home/username) on Linux/Solaris.

1.3. IDE Settings for Payara Server

Payara Server is controlled by major IDEs with some settings.

1.3.1. Eclipse

In case of Eclipse , you should GlassFish Tools contained in Oracle Enterprise Pack for Eclipse (OEPE) (It's no problem to contain other plugins of OEPE). Choose GlassFish 4 for server type, and input C:\payara41\glassfish (Windows) or /opt/payara41/glassfish (Linux/Solaris) for installed directory. Pay attention to setting it glassfish directory that is a sub directory of Payara's install directory. (There's Nucleus, the core modules of Payara) Other choices may be still in defaults.

You may also obtain GlassFish Tools from Eclipse Marketplace.

1.3.2. NetBeans

NetBeans can integrate with GlassFish using a built in feature. Add a server instance in Service - Servers. Choose GlassFish Server as server type, and input C:\payara41 (Windows) or /opt/payara41 (Linux/Solaris) as installed directory. Other choices may be still in defaults. Note: NetBeans can download a server instance if not exists. But it never use for Payara because it will download GlassFish.

1.3.3. IntelliJ IDEA

IntelliJ IDEA can integrate with GlassFish using a built in feature. At first, choice Glassfish Server on Default Settings -> Build, Execution, Deployment -> Application Servers. And then required Glassfish Home directory, input C:\payara41 (Windows) or /opt/payara41 (Linux/Solaris). Other choices may be still in defaults.

1.4. Using Payara Server as development environment

Payara Server in development is usually started, stopped and deployed from IDE. Thus you may know following three operations for Payara Server.

  • payara41/bin/asadmin start-domain to start Payar Server
  • payara41/bin/asadmin stop-domain to stop Payara Server
  • Using admin console http://localhost:4848/ to operate Payara Server during Payara Server is running

Payara's admin console is such a Web UI;

payara-admingui-top.png
figure 1 - Payara Server Console (Dev.)

2. Instruction of operating envirionment

2.1. Downloading Payara Server

Download Payara Server from http://www.payara.fish/downloads as same as instruction of developing envirionment.

In case of Payara 4.1.1.171.1 (latest release at the time), you may select following one;

  • Payara 4.1.1.171.1 (Full) : payara-4.1.1.171.1.zip
  • Payara-Web 4.1.1.171.1 (Web Profile) : payara-web-4.1.1.171.1.zip
  • Payara-ML 4.1.1.171.1 (Multi-Language Full) : payara-ml-4.1.1.171.1.zip
  • Payara-Web-ML 4.1.1.171.1 (Multi-Language Web Profile) : payara-web-ml-4.1.1.171.1.zip

In case of operating environment, you should select to install either Full Platform or Web Profile. For many purposes, Web Profile is better. If you want to use APIs without Web Profile, you need to install Full Platform.

See also following slides about Web Profile.

It's easy to operate Payara Server using mainly admin console. You may use localized admin console to convenient. Then you may install Web Profile Multi-language : payara-ml-4.1.1.171.1.zip.

Tips: When you post an issue in trouble, it's precondition to use English UI. Thus I suggest you also prepare English version for reproducing tests.

2.2. Installation of Payara Server

Extract payara-4.1.1.171.1.zip to any directory. But on Windows, avoid to the directory it's named spaces (e.g. C:\Program Files). If you are an administrator of the computer, extract to C:\ (on Windows) or /opt (on Linux/Solaris) for ease. Then Payara Server installed on C:\payara41 (on Windows) or /opt/payara41 (on Linux/Solaris).

2.3. Enable to remote access to Payara Server

2.3.1. Setting the admin password

Payara is not set the admin pasword in default. It's mean that remote access to Payara Server is disabled in default. Then, at first, you should set the admin password. Use asadmin change-admin password command to set the admin password. see follows;

C:\payara41\bin>asadmin change-admin-password
Enter admin user name [default: admin]>
Enter the admin password>
Enter the new admin password>
Enter the new admin password again>
Command change-admin-password executed successfully.

asadmin change-admin-password command works two tasks, (1) set admin user name, (2) set or change admin password. The input is following items;

  1. admin user name: If use 'admin', you may empty this item
  2. the admin password: current password; In default, it must set empty because the default admin password is empty
  3. the new admin password: input the password
  4. the new admin password again: input the password again

Attention: The first item is admin user name (not password), don't miss.

2.3.2. Enable secure administration

Payara rejects remote access via HTTP (plain text), so you should enable secure administration (remote access via HTTPS). To enable or disable secure administration is needed to run Payara Server, you start to use asadmin start-domain command.

Then you enable secure administration using asadmin enable-secure-admin command. the command requires admin user name and password. because you should set the admin password the previous step.

C:\payara41\bin>asadmin enable-secure-admin
Enter admin user name>  admin
Enter admin password for user "admin">
You must restart all running servers for the change in secure admin to take effect.
Command enable-secure-admin executed successfully.

After secure administration is enabled, you have to reboot Payara Server. You may use convenient command, asadmin restart-domain.

To enable and disable secure administration is also supported by admin console. But Web browser maybe disabled on Payara Server's machine. So I suggest to use command line.

2.4. Using Payara Server as operating envirionment

It's easest that (1) Start to asadmin start-domain command, (2) Stop to asadmin stop-domain command, (3) Other operations are on admin console. Admin console URL via remote access is https://<hostname>:4848/ and require 'User Name' (admin user name, 'admin' in default) and 'Password' (the admin password).

payara-admingui-login.png
figure 2 - Payara Server Log-in (Admin Console)

Set enable-secure-admin when to enable remote access. It means that you access to admin console via HTTPS (HTTP over SSL/TLS) instead of HTTP and using encrypted connection. (To access to admin console is restricted via HTTPS since GlassFish 3.1.2?) Web browsers will block to access admin console once because key store of Payara Server is not ready. But even if it's in intranet, you can ignore and force access to admin console.

payara-admingui-top.png
figure 3 - Payara Server Console (Ops.)

3. Starting up Payara Micro

3.1. Downloading Payara Micro

3.1.1. Download from the Web site

Download Payara Micro 171 (payara-micro-4.1.1.171.1.jar) from http://www.payara.fish/downloads (If need, you may change the file name to any).

3.1.2. Maven integration

Add dependencies to pom.xml in a Maven Project as following. Then Payara Micro is downloaded automatically from Maven Central Repository.

<dependencies>
  <groupId>fish.payara.extras</groupId>
  <artifactId>payara-micro</artifactId>
  <version>4.1.1.171.1</version>
</dependencies>

3.2. Using Payara Micro as operating environment

3.2.1. Run Payara Micro

At first, prepare a WAR file of Web application; webapp.war. Basically you develop a Web application using Payara Server even if you run it on Payara Micro.

And then, run Payara Micro on a terminal.

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war

Ctrl + C to exit.

Payara Micro is enabled Hazelcast clustering in default. (Payara Server is disabled in default) Usually it need not change. If you set with --noCluster option to disable Hazelcast clustering and start up first such a little. But --noCluster option stops whole of Hazelcast, it's disabled not only automatic clustering but also JCache.

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war --noCluster

3.2.2. Create Uber Jar and run

You can create Uber Jar (webapp.jar) from Payara Micro and WAR files.

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war --packageUberJar webapp.jar

After create Uber Jar, you run it directly.

java -jar webapp.jar

Uber Jar is Payara Micro includes WAR files. You may use various options of Payara Micro, and Ctrl + C to exit.

4. Conclusion

Payara is growing day by day. Recently it is not only "well-maintained GlassFish" but also fitting for commertial use. Now it's features are so many and powerful, but it's minimal instruction to use is kept simple. I wish Payara is used many people -- from newbies to experts.

Payara クイック・スタート・ガイド

この記事は、2016 年 12 月 3 日の「Payara クイック・スタート・ガイド」の改訂版です。

最近、私の周りで Payara ユーザーが増えてきました。その多くは GlassFish から移行してきた方々ですが、今後 Payara が最初の Java EE サーバーとなる方々も出てくるかと思います。そういった方々に向けて、Payara の実用レベルで最低限のセットアップをまとめておきます。

0. 事前準備

0.1. JDK

Payara を動作させるには JDK 7 または JDK 8 Update 20 以降が必要です。OS によってセットアップ手順が異なるため割愛しますが、できるだけ新しい Update を使用することをおすすめします。なお、JVM (java / java.exe) へのパスはあらかじめ設定しておいてください (注意: Payara は JAVA_HOME 環境変数を参照しません)。

0.2. ネットワーク設定

いくつかの OS では既定でほとんどのポートの受信を拒否するよう設定されています。これらの OS では、Payara で使用する TCP ポートの受信を許可するよう設定変更が必要です。Payara が使用する主なポートは、TCP 4848、8080、8181 です。Hazelcast を有効にする場合 (Payara Micro の場合は必須) はそれに加えて 5900 以降 (最大 5999) を空けておきます。

1. 開発環境向けスタートアップ・ガイド

1.1. Payara Server をダウンロードする

Payara Server をダウンロードします。http://www.payara.fish/downloads (多国語化版が欲しい場合は http://www.payara.fish/all_downloads) から無償で入手できます。ユーザー登録なども必要ありません。運用環境として Payara Micro を使用する場合でも、開発環境での作業は Payara Server で行った方が便利です。

Payara 4.1.1.171.1 (本稿執筆時点の最新版) の場合、基本的には以下のいずれかを選択することになります。

  • Payara 4.1.1.171.1 (Full) : payara-4.1.1.171.1.zip
  • Payara-Web 4.1.1.171.1 (Web Profile) : payara-web-4.1.1.171.1.zip
  • Payara-ML 4.1.1.171.1 (Multi-Language Full) : payara-ml-4.1.1.171.1.zip
  • Payara-Web-ML 4.1.1.171.1 (Multi-Language Web Profile) : payara-web-ml-4.1.1.171.1.zip

開発環境の場合は Full Platform の payara-4.1.1.171.1.zip を選択すると良いでしょう。必要に応じて (例えば、Payara Micro での運用を想定している場合など)、Maven の依存関係で javax:javax-ee-web:7.0 を設定して使用可能な API を Java EE Web Profile に制限する方法を採るのが現実的です。

1.2. Payara Server をインストールする

ダウンロードした payara-4.1.1.171.1.zip を展開します。展開先は自由に選べます (ただし Windows 環境では C:\Program Files などスペース入りのフォルダは避けた方が無難です)。管理者権限がある場合は、Windows では C:\ 以下に、Linux/Solaris では /opt 以下にそれぞれ展開すると分かりやすいです。展開後、Windows では C:\payara41 以下、Linux/Solaris では /opt/payara41 以下に Payara Server が配置されます。この状態でインストールは完了です。

管理者権限を持たない場合は、Windows では %USERPROFILE% (C:\Users\{ユーザー名}) 以下、Linux/Solaris では $HOME (/export/home/{ユーザー名} または /home/{ユーザー名}) 以下にそれぞれ展開すると良いでしょう。

1.3. IDE で Payara Server の設定を行う

Payara Server を IDE で制御できるように設定します。

1.3.1. Eclipse

Eclipse の場合は、Oracle Enterprise Pack for Eclipse (OEPE) に含まれる GlassFish Tools をインストールする必要があります (OEPE の他のプラグインが含まれていても問題ありません)。サーバーの種類として GlassFish 4 を選択し、サーバーのインストール先として C:\payara41\glassfish (Windows) または /opt/payara41/glassfish (Linux/Solaris) を選択します。Payara のインストール・ディレクトリ以下の glassfish サブディレクトリ (この場所が Payara のコアである Nucleus になります) まで指定する必要があることに注意してください。その他の設定項目は、既定値のままで良いでしょう。

なお、GlassFish Tools は Eclipse Marketplace からも入手することができます。

1.3.2. NetBeans

NetBeans には GlassFish との統合機能が含まれているため、これを利用します。サービス - サーバー画面で、サーバー・インスタンスを追加します。サーバーの種類として GlassFish Server を選択し、サーバーのインストール先として C:\payara41 (Windows) または /opt/payara41 (Linux/Solaris) を入力します。その他の設定項目は、既定値のままで良いでしょう。なお、NetBeans にはサーバー・インスタンスがない場合にダウンロードしてインストールする機能も備わっていますが、Payara の場合には使用できません (GlassFish がダウンロードされるため)。

1.3.3. IntelliJ IDEA

IntelliJ IDEA には GlassFish との統合機能が含まれているため、これを利用します。Default Settings -> Build, Execution, Deployment -> Application Servers で Glassfish Server を選択します。Glassfish Home ディレクトリをたずねられますので、C:\payara41 (Windows) または /opt/payara41 (Linux/Solaris) を入力します。その他の設定項目は、既定値のままで良いでしょう。

1.4. Payara Server を開発環境として使用する

開発環境の Payara Server は、通常 IDE から起動、停止およびアプリケーションのデプロイを行います。Payara Server の直接操作については、以下の 3 点を押さえておけば大丈夫でしょう。

  • payara41/bin/asadmin start-domain で Payar Server を起動
  • payara41/bin/asadmin stop-domain で Payara Server を停止
  • Payara Server の起動中は管理コンソール http://localhost:4848/ で大抵の設定操作は可能

Payara の管理コンソールは、以下のような Web UI となっています (英語版 UI)。

payara-admingui-top.png
figure 1 - Payara Server Console (Dev.)

2. 運用環境向けスタートアップ・ガイド

2.1. Payara Server をダウンロードする

Payara Server をダウンロードします。手順は開発環境の場合と同じです。Payara 4.1.1.171.1 の場合、基本的には以下のいずれかを選択することになります。

  • Payara 4.1.1.171.1 (Full) : payara-4.1.1.171.1.zip
  • Payara-Web 4.1.1.171.1 (Web Profile) : payara-web-4.1.1.171.1.zip
  • Payara-ML 4.1.1.171.1 (Multi-Language Full) : payara-ml-4.1.1.171.1.zip
  • Payara-Web-ML 4.1.1.171.1 (Multi-Language Web Profile) : payara-web-ml-4.1.1.171.1.zip

運用環境の場合は、まず Full Platform と Web Profile のどちらをインストールするかの判断が必要です。多くの用途では Web Profile が第一選択肢となるでしょう。Web Profile にない API が必要であれば Full Platform をインストールします。

Web Profile については以下の資料をしてください。

Payara Server の設定操作は管理コンソールを中心に行うのが便利です。その際、日本語化された UI は使い勝手が良いため、多国語版 Web Profile : payara-ml-4.1.1.171.1.zip を選択しても良いでしょう。

ただし、トラブル発生時の Issue は英語版 UI での利用が前提であるため、再現テストのために英語版 Web Profile 環境を別途用意しておくと安心です。

2.2. Payara Server をインストールする

ダウンロードした Payara Server の ZIP アーカイブ (payara-ml-4.1.1.171.1.zip など) を展開します。展開先は自由に選べます (ただし Windows 環境では C:\Program Files などスペース入りのフォルダは避けた方が無難です)。管理者権限がある場合は、Windows では C:\ 以下に、Linux/Solaris では /opt 以下にそれぞれ展開すると分かりやすいです。展開後、Windows では C:\payara41 以下、Linux/Solaris では /opt/payara41 以下に Payara Server が配置されます。この状態でインストールは完了です。

2.3. Payara Server のリモートアクセスを有効にする

2.3.1. 管理パスワードの設定

Payara の既定では、管理パスワードが設定されていません。リモートアクセスを有効にするのに先立って、まず管理パスワードを設定する必要があります。管理パスワードの設定には asadmin change-admin-password コマンドを使用します。

C:\payara41\bin>asadmin change-admin-password
Enter admin user name [default: admin]>
Enter the admin password>
Enter the new admin password>
Enter the new admin password again>
Command change-admin-password executed successfully.

ここでは、管理ユーザー名の決定と、管理パスワードの変更という 2 種類の操作を行います。入力項目は、以下の通りです。

  1. 管理ユーザー名: 既定の admin を使用する場合は空欄とします
  2. 現在の管理パスワード: 既定では管理パスワードは設定されていないため空欄とします
  3. 新しい管理パスワード: 管理パスワードを入力します
  4. 新しい管理パスワード: 入力した管理パスワードと同じものを確認のため入力します

最初に管理ユーザー名の設定があります。ここは見落としがちのため注意してください。

2.3.2. セキュア管理の有効化

リモートアクセスのためにセキュア管理 (HTTPS 通信) を有効化します。セキュア管理の有効化/無効化は Payara Server が起動している必要があるため、まず asadmin start-domain コマンドで Payara Server を起動します。

続いて、asadmin enable-secure-admin コマンドでセキュア管理を有効化します。その際には、先に設定した管理ユーザー名 (admin) と管理パスワードを入力する必要があります。

C:\payara41\bin>asadmin enable-secure-admin
Enter admin user name>  admin
Enter admin password for user "admin">
You must restart all running servers for the change in secure admin to take effect.
Command enable-secure-admin executed successfully.

セキュア管理を有効化した後は、Payara Server を再起動する必要があります。Payara Server の停止と起動を組み合わせても良いのですが、再起動を行う asadmin restart-domain コマンドを使用した方が高速です。

セキュア管理の有効化/無効化は管理コンソールでも操作できますが、Payara Server のインストール先が必ずしも Web ブラウザを備えているとは限らないため、コマンドラインから管理パスワードと同時に設定を行った方が無難です。

2.4. Payara Server を運用環境として使用する

手っ取り早い方法は、起動コマンドの asadmin start-domain と停止コマンドの asadmin stop-domain だけ覚えて、後の操作は管理コンソール上で行うことです。リモートアクセス時の管理コンソールの URL は https://<hostname>:4848/ となり、起動時にユーザー名 & パスワードの認証があります (ユーザー名は既定では admin となります)。

payara-admingui-login.png
figure 2 - Payara Server Log-in (Admin Console)

リモートアクセスを有効にする際に、enable-secure-admin を設定していますが、これにより管理コンソールへのアクセスが HTTP から HTTPS (HTTP over SSL/TLS) に変更され、暗号化通信となります (GlassFish 3.1.2 あたりから管理コンソールへのリモートアクセスは HTTPS に限定されるようになっています)。Payara Server のインストール直後は電子証明書のチェーンが整備されていないため、ブラウザ側で一旦アクセスをブロックされますが、イントラネット上であるため無視してアクセスを強行してください。その後、Payara の管理コンソールにアクセスできるようになります。

payara-admingui-top.png
figure 3 - Payara Server Console (Ops.)

3. Payara Micro スタートアップ・ガイド

3.1. Payara Micro をダウンロードする

3.1.1. Web サイトからのダウンロード

http://www.payara.fish/downloads から Payara Micro 171 (payara-micro-4.1.1.171.1.jar) をダウンロードします。ダウンロード後、ファイル名は必要に応じて変更しても構いません。

3.1.2. Maven 統合

Maven プロジェクトの pom.xml に以下の依存関係記述を追加します。これにより、Maven Central Repository から Payara Micro が自動的にダウンロードされ、プロジェクトに組み込まれます。

<dependencies>
  <groupId>fish.payara.extras</groupId>
  <artifactId>payara-micro</artifactId>
  <version>4.1.1.171.1</version>
</dependencies>

3.2. Payara Micro を運用環境として使用する

3.2.1. Payara Micro を通常実行する

まず、Web アプリケーションの WAR ファイルを用意します。ここでは WAR ファイルを webapp.war と仮定します。Web アプリケーションは基本的に Payara Server を使って開発することになるでしょう。

WAR ファイルが準備できたら、ターミナルから Payara Micro を起動します。

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war

終了する場合は Ctrl + C を使います。

Payara Micro は既定で Hazelcast による自動クラスタリングが有効になっています (Payara Server では無効)。通常はそのままでも問題ありませんが、--noCluster オプションを付けて無効化することで、少しだけ起動を早めることができます。ただし、--noCluster オプションを使用すると、Hazelcast そのものが無効化されるため、自動クラスタリングだけでなく JCache も使えなくなってしまうことに注意してください。

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war --noCluster

3.2.2. Uber Jar を作成して実行する

Payara Micro と WAR ファイルから Uber Jar を作成することもできます。ここでは、出力する Uber Jar の名前を webapp.jar と仮定します。

$ java -jar payara-micro-4.1.1.171.1.jar --deploy webapp.war --packageUberJar webapp.jar

作成後は、直接 Uber Jar を実行することになります。

java -jar webapp.jar

Payara Micro の Uber Jar は、実際には WAR ファイルを内包した Payara Micro です。Payara Micro の起動時オプションが使用できるほか、Ctrl + C で終了できる点も共通しています。

4. まとめ

Payara は日々進化を続けており、当初の「より良い GlassFish」から、本格的な商用 Java EE サーバーとなるべく様々な機能が追加されています。しかし、最小限の設定は決して多くなく、今回ご紹介した内容をもとに簡単な環境構築は可能です。

Payara Micro の GAV デプロイ

この記事は Payara Advent Calendar 2016 の 20 日目です。昨日は @backpaper0 さんの「PayaraをDockerで動かす」です。昨年の前半に Payara の Mike Croft (@croft) が中心となって進めていたプロジェクトの成果を日本語で確かめるよい機会です。Docker 環境をすぐに使用できる方は是非お試しください。

去る 12 月 3 日に開催された JJUG CCC 2016 Fall にて、「Payara Micro の設計と実装」と題した発表を行いました。その後のフィードバックで、Maven リポジトリ上の WAR ファイルを Payara Micro へ直接デプロイできることに驚かれた方も少なくなかったようなので、今回はごく簡単なサンプルを用いて、Maven リポジトリからのデプロイ (GAV デプロイ) についてお話ししようと思います。初回こそ Maven 環境を整えるという面倒な作業を要しますが、一度環境を整備すればその後は楽に作業を進められます。

1. 事前準備: WAR ファイルの作成とリポジトリへの登録

まずは WAR ファイルをアップロードする Maven リポジトリが必要です。Maven Central が使用できるのであれば、それを使用するのが最も簡単です。Maven Central へのアップロード方法については http://maven.apache.org/guides/mini/guide-central-repository-upload.html を参照してください。Maven Central は使用できないが独自にホスティング環境を持っている場合には、Sonatype Nexus で Maven リポジトリを構築すると良いでしょう。

私は Maven Central へのアップロード経験がなく、かの場所のワークフローも面倒に思えたので、VPS (ConoHa) を借りて Nexus をインストールしました (MSDN の Azure にも Nexus をホスティングしているのですが、通信量の制約が...)。

アップロードする WAR は、Payara Micro で動作するものであれば何でも良いです。GAV デプロイでも必要であれば複数の WAR をデプロイすることができます。ただし、Snapshot バージョンの場合は設定次第で苦戦するかもしれませんので、それを避けたければ Release バージョンをアップロードしましょう。

2. Payara Micro API を用いた GAV デプロイの実際

今回はとりあえず、JAX-RS で "Hello, world" を出力する WAR ファイルを作りました。コードは https://github.com/khasunuma/helloweb/tree/version-1.0 にあります (実際は HTML/XML/JSON の 3 出力形式に対応するとか、余計なことをしています)。この WAR ファイルは私がホスティングしている Nexus にアップロード済みです。

まずはコマンドラインから、以下のように実行してください。

java -jar payara-micro-4.1.1.164.jar --deployFromGAV jp.coppermine.samples:helloweb:1.0.0 --additionalRepository http://repo1.cubiwano.org/repository/maven-public

--deployFromGAV で WAR ファイルの groupId、artifactId および version を指定します。また、Maven Central 以外のリポジトリ (特に in-house リポジトリ) にある場合は、その URL (今回は http://repo1.haswell.jp/repository/maven-public) を指定します。これだけでデプロイはできるはずです。

【注意】 上記の Maven リポジトリ (repo1.cubiwano.org) は HTTP と HTTPS の両方に対応させていますが、GAV デプロイの際は "HTTP" を指定してください。

このサンプルの動作確認は、ブラウザで http://localhost:8080/helloweb/api/hello を開くことでできます。"Hello, world" が出力されていれば OK です。

Payara Micro API を使用する場合は、以下のようにコーディングします (完全なコードは https://github.com/khasunuma/payaramicro-gav にあります)。

package jp.copermine.samples.payaramicro;

import fish.payara.micro.BootstrapException;
import fish.payara.micro.PayaraMicro;

public class GavDeploy {

    public static void main(String[] args) throws BootstrapException {
        // Required if not using Maven central repository 
        final String repositoryUrl = "http://repo1.cubiwano.org/repository/maven-public";
        
        // groupId , artifactId , version
        // if version is SNAPSHOT, it may not run well
        final String gav = "jp.coppermine.samples,helloweb,1.0.0";
        
        PayaraMicro.getInstance().addRepoUrl(repositoryUrl).addDeployFromGAV(gav).setNoCluster(true).bootStrap();
    }

}

明日は @wreulicke さんです。

この記事は、Payara Advent Calendar 2016JavaFX Advent Calendar 2016Java EE Advent Calendar 2016 の 15 日目です。昨日は、

です。

Payara Micro は Java EE の埋め込みサーバーとして使用することが可能です。Java EE の埋め込みサーバーは、Java SE アプリケーションに対して Java EE API を提供するもので、別途 Java EE サーバーを構築しなくても Java SE アプリケーション上で Web アプリケーションをはじめとする Java EE アプリケーションを実行させることができるようになります。また、Java EE サーバーを準備しなくて良いことから、Java EE アプリケーションの単体テストを実行するための組み込みコンテナとしても活用することができます。

埋め込みサーバーとしての Payara Micro は、Embedded GlassFish Web Profile を使いやすくラップしたものであり、API としては Web Profile に加えて JBatch、Concurrency Utilities、JCache が追加されています (ただし、クラスタリングを無効にすると Hazelcast が使われなくなるため、それによって提供される JCache も同時に使用できなくなります)。

Payara Micro を埋め込みサーバーとして使用する場合、通常は Java SE のアプリケーションに組み込んで使うことになりますが、JavaFX のアプリケーションに組み込むことも可能です。ただし、いくつか制約事項がありますので、以下に示します。

1. スタートアップ

PayaraMicro のスタートアップ・コードは、javafx.application.Application#init メソッドに記述します。この場所でないと Payara Micro を初期化することができません。

private PayaraMicroRuntime runtime;

@Override
public void init() throws Exception {
    runtime = PayaraMicro.getInstance().setNoCluster(true).bootStrap();
}

2. アプリケーション実行時

このセクションは適当に流すつもりだったのですが、3 つの Advent Calendar に対する共通エントリとしたため気が引けて、当日未明になってから 5 時間がかりで全面的に書き直しました。何らかの参考になれば幸いです。

FXML と Controller クラスを使用しているのであれば、Java SE から Web アプリケーションに対してリクエストを送信し、そのレスポンスを受信して UI に反映させる処理となります。ここでは JAX-RS Client と WebSocket の使用例を重点的に示します。

2.1. JAX-RS Client を使用する場合

以下に JAX-RS のクライアント/サーバーを利用した例を示します。

Step 1 - JAX-RS のエンドポイントを定義する

まず、Web アプリケーション側に JAX-RS のエンドポイントを定義します。サンプルコードを以下に示します。

// ApplicationPath クラス
@ApplicationPath("api")
public class ApplicationConfig extends Application { }

// SearchResource クラス
@Path("search")
@RequestScoped
public class SearchResource {
  @GET
  @Path
  @Produces("application/xml")
  public List<List> search(@NotNull @QueryParam("q") String query) {
    List result = new ArrayList<>();
    
    // (ここで検索処理を行い、結果の文字列を result へ格納する)
    
    // JAX-RS は List<String> でも適切な XML にアンマーシャリングする
    return result;
  }
}

Step 2 - JavaFX から JAX-RS エンドポイントへのアクセスを実装する

JavaFX の Controller に対して JAX-RS エンドポイントにアクセスするコードを記述します。JAX-RS に関しては、これだけで JavaFX からデータを取得して ListView などへの表示を行えるようになります。


public class SearchController implements Initializable {
  
  //////////////// 共通処理 ////////////////
  
  /**
   * 検索条件を入力するテキストフィールドです。
   */
  @FXML
  private TextField queryField;
  
  /**
   * 検索結果の文字列を格納するリストです。
   */
  private List results = new ArrayList<>();
  
  //////////////// 検索処理 (非同期) の定義 ////////////////

  /**
   * 検索実行可能な場合に true となるプロパティです。
   */
  private BooleanProperty ready = new SimpleBooleanProperty(true);
  
  /**
   * JAX-RS エンドポイントを呼び出すタスクを作成します。
   * <p>
   * ボタン・クリックのイベントが発生したときに実行することを想定しているため、
   * {@link Service} による非同期処理として実装する必要があります。
   */
  Service<List> searchService = new Service<List>() {
    @Override
    protected Task<List> createTask() {
      return new Task<List>() {
        @Override
        protected List call() throws Exception {
          // 検索条件は queryField に入力される
          String query = queryField.getText();
          
          // 検索結果は listView に設定する
          listView.getItems().clear();
          
          // Web サービスを呼び出し、検索結果を取得する
          List results = client.target(CONTEXT_ROOT).path("api").path("search")
                                       .queryParam("q", query)
                                       .request()
                                       .accept("application/xml")
                                       .get(new GenericType<List>() { });
          // (追加の処理が必要な場合は、ここで実装する)
        
          // 検索結果を設定する
          // このクラス内では getValue() で参照可能になる
          return result;
        }
      };
    }
    
    /**
     * タスク実行時の処理を記述します。
     */
    @Override
    protected void running() {
      // 再検索を行う時は、前回の検索結果を消去する
      tweets.clear();
      
      // 検索中なので ready プロパティの状態を false に変更する
      // これを行わないと検索リクエストが無制限に発生して面倒なことになる
      ready.set(false);
    }
    
    /**
     * タスク成功時の処理を記述します。
     */
    @Override
    protected void succeeded() {
      // 検索結果を取得する
      results.addAll(getValue());
      
      // 検索結果を listView に設定する (重複排除とソートは実施する)
      listView.setItems(FXCollections.observableList(
          getValue().stream().distinct().sort().collect(toList());
      
      // (検索結果のダイアログ表示などは、この場所に記述すること)
    
      // ready プロパティの状態を true に変更して、検索可能な状態に戻す
      ready.set(true);
    }
    
    /**
     * タスクキャンセル時の処理を記述します。
     */
    @Override
    protected void cancelled() {
      // (検索キャンセルのダイアログ表示などは、この場所に記述すること)
      
      // ready プロパティの状態を true に変更して、検索可能な状態に戻す
      ready.set(true);
    }
    
    /**
     * 検索失敗時の処理を記述します。
     */
    @Override
    protected void failed() {
      // (検索失敗のダイアログ表示などは、この場所に記述すること)
      
      // ready プロパティの状態を true に変更して、検索可能な状態に戻す
      ready.set(true);
    }
  };
  
  //////////////// ボタン・イベント処理の定義 ////////////////
  
  /**
   * 検索ボタンをクリックした時の処理を記述します。
   * 
   * @param event アクション実行時のイベント
   */
  @FXML
  public void onSearchAction(ActionEvent event) {
    // searchService による非同期呼び出し
    // もし仮に同期呼び出しを使用すると、JavaFX の UI が硬直するので注意
    searchService.reset();
    searchService.start();
  }
  
  @Override
  public void initialize(URL location, ResourceBundle resources) {
    // 省略
  }  
}

2.2. WebSocket

以下に WebSocket のクライアント/サーバーを利用した例を示します。

Step 1 - WebSocket のエンドポイントを定義する

Web アプリケーション側で WebSocket のエンドポイントを Singleton として実装します。Singleton 以外でも実装可能のはずですが、私の環境では Singleton が最も安定して動作します。

@Singleton
@ServerEndpoint(value = "/api/twitter/publish")
public class TwitterPublisher {
  
  /**
   * WebSocket のセッションを管理するオブジェクトです。
   */
  private Set sessions = new CopyOnWriteArraySet<>();
  
  /**
   * 新たな WebSocket 接続があったときに対応するセッションを追加します。
   *
   * @param 追加されるセッション
   */
  @OnOpen
  public void onOpen(Session session) {
    sessions.add(session);
  }
  
  /**
   * 既存の WebSocket 接続が切断されたときに対応するセッションを削除します。
   *
   * @param 削除されるセッション
   */
  @OnClose
  public void onClose(Session session) {
    sessions.remove(session);
  }
  
  /**
   * エラー発生時の処理を定義します。
   *
   * throws t エラー内容を表す例外オブジェクト
   */
  @OnError
  public void onError(Throwable t) {
    // ログ出力などを行う (省略)
  }
  
  /**
   * 接続済みのすべてのセッションに対してメッセージを配信します。
   * <p>
   * このメソッドは、メッセージ配信のタイミングで外部から呼ばれます。
   * このサンプルでは、外部の EJB タイマーから定期的に呼び出します。
   */
  public void send(String message) {
    sessions.forEach(session -> {
      try {
        session.getBasicRemote().sendText(message);
      } catch (IOException) {
        e.printStackTrace();
      }
    });
  }
  
  @Override
  public void initialize(URL location, ResourceBundle resources) {
    // 省略
  }  
}

Step 2 - Web アプリケーション側に定期配信の仕組み (EJB Timer) を実装する。

WebSocket は定期的に配信するところに強みがあります。エンドポイント自身には定期配信を行うための仕組みがないため、エンドポイントを呼び出して定期配信するためのコードを実装する必要があります。ここでは Web アプリケーション側に EJB Timer を追加して、10 秒おきにエンドポイントからデータを取得するようなコードを用意しました。

@Singleton
public class Scheduler {
  
  /**
   * ポーリング実行時の絞り込み条件を表します。
   */
  private String query;
  
  /**
   * ポーリングの開始・停止を制御します。
   *
   * @pamam flag {@code true} の場合はポーリング実行、{@code false} の場合は停止
   */
  public setPolling(boolean flag) { /* 省略 */ }
  
  /**
   * ポーリングの開始・停止の状態を取得します。
   *
   * @return {@code true} の場合はポーリング実行中、{@code false} の場合は停止済
   */
  public isPolling() { /* 省略 */ }
  
  /**
  * ポーリングを開始します。
  *
  * @param query ポーリング対象の絞り込み条件
  */
  public void startPolling(String query) {
    // ポーリング停止中のみ処理対象 (既に実行中の場合は何もしない)
    if (!isPolling()) {
      this.query = query;
      setPolling(true);
    }
  }
  
  /**
   * ポーリングを停止します。
   */
  public void stopPolling() {
    // ポーリング実行中のみ処理対象 (既に停止中の場合は何もしない)
    if (isPolling()) {
      setPolling(false);
      query = null;
    }
  }
  
  /**
   * Publisher クラス (WebSocket エンドポイント) のインスタンスです。
   */
  @Inject
  private Publisher publisher;
  
  /**
   * 定期的に {@link Publisher#send(String)} を呼び出してデータを配信します。
   * このメソッドは EJB Timer として呼び出されることが補償されているため、
   * {@code private} スコープとしています。
   * <
   * なお、このメソッドは 10 秒おきに EJB Timer として呼び出されます。
   *
   * @see javax.ejb.Scledule
   */
  @Schedule(hour = "*", minute = "*", second = "*/10", persistent = false)
  private void polling() {
    if (isPolling()) {
      // ポーリング実行中: データ配信
      // ここでは getNewResult メソッドで新着メッセージを取得すると仮定
      List result = getNewResult(query);
      result.forEach(s -> publisher.send(s));
    }
  }
}

Step 3 - JavaFX から WebSocket エンドポイントへのアクセスを実装する

JavaFX の Controller に対して WebSocket エンドポイントにアクセスするコードを記述します。WebSocket に関しては、これだけで JavaFX からデータを取得して ListView などへの表示を行えるようになります。

public static class PollingController implements Initializable {

//////////////// 共通処理 ////////////////

/**
 * ポーリング対象条件を入力するテキストフィールドです。
 */
@FXML
private TextField queryField;

/**
 * ポーリングの開始・終了を制御するトグルボタンです。
 */
@FXML
private ToggleButton pollingButton;

/**
 * ポーリング結果の文字列を格納するリストです。
 */
@FXML
private ListView<String> listView;

//////////////// 検索処理 (非同期) の定義 ////////////////

/**
 * ポーリング実行可能な場合に true となるプロパティです。
 */
private BooleanProperty ready = new SimpleBooleanProperty(true);

/**
 * WebSocket エンドポイントを呼び出すタスクを作成します。
 * <p>
 * ボタン・クリックのイベントが発生したときに実行することを想定しているため、
 * {@link Service} による非同期処理として実装する必要があります。
 */
Service<Void> pollingService = new Service<Void>() {
  @Override
  protected Task<Void> createTask() {
    return new Task<Void>() {
      /**
       * This latch works the thread keep awaiting because of accepting cancel request.
       */
      private CountDownLatch messageLatch = new CountDownLatch(1);
      
      /**
       * WebSocket セッションへの参照を表します。
       */
      private Session session;
      
      @Override
      protected Void call() throws Exception {
        // WebSocket のエンドポイント
        final wsEndpoint = URI.create("/api/polling/publish");
        
        // このあたりは WebSocket クライアント API を使うときのほぼ定型コード
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        session = container.connectToServer(new Subscriber(listView), wsEndpoint);
        
        messageLatch.await();
        
        return null;
      }
      
      /**
       * ポーリング実行時の処理を記述します。
       */
      @Override
      protected void cancelled() {
        if (session != null) {
          try {
            session.close();
          } catch (IOException e) {
            // ログ出力などを行う (省略)
          }
        }
        
	// pollingButton をクリック可能にする
        pollingButton.setSelected(false);
        
        // ポーリング実行を受け付ける
        ready.set(true);
      }

      /**
       * ポーリングキャンセル時の処理を記述します。
       */
      @Override
      protected void failed() {
        
	// pollingButton をクリック可能にする
        pollingButton.setSelected(false);
        
        // ポーリング実行を受け付ける
        ready.set(true);
      }
    };
  }

  /**
   * タスク失敗時の処理を記述します。
   */
  @Override
  protected void cancelled() {
    // pollingButton をクリック可能にする
    pollingButton.setSelected(false);
    
    // ポーリング実行を受け付ける
    ready.set(true);
  }

  /**
   * タスク失敗時の処理を記述します。
   */
  @Override
  protected void failed() {
    // pollingButton をクリック可能にする
    pollingButton.setSelected(false);
    
    // ポーリング実行を受け付ける
    ready.set(true);
  }
};

/**
 * ポーリング開始・停止ボタンをクリックした時の処理を記述します。
 * 
 * @param event アクション実行時のイベント
 */
@FXML
public void onPollingAction(ActionEvent event) {
  if (pollingButton.isSelected()) {
    pollingButton.setSelected(false);
    
    // 開始要求の GET リクエストを送信するだけ: レスポンスは無視
    client.target(CONTEXT_PATH).path("api").path("polling").path("start")
          .queryParam("q", queryField.getText()).request().get();

    listView.getItems().clear();
    
    // pollingService による非同期呼び出し
    // もし仮に同期呼び出しを使用すると、JavaFX の UI が硬直するので注意
    pollingService.reset();
    pollingService.start();
  } else {
    pollingService.cancel();
    
    // 停止要求の GET リクエストを送信するだけ: レスポンスは無視する
    client.target(CONTEXT_PATH).path("api").path("polling").path("stop")
          .request().get();
  }
}

2.3. その他の手段

JavaServer Faces、Servlet、JSP などに対しては、画面遷移を伴うことから、通常は JavaFX の WebView を経由したアクセスになります。ただし、ユースケースによっては HTTP Client で直接アクセスすることもあり得ます。

JAX-WS については、Java SE に JAX-WS Client が付属しているため、これを使用することができます。

RMI-IIOP などのレガシーな分散処理プロトコルについても、検証こそしていませんが同様の要領で使用できるのではないかと考えています。もっとも、今どき RMI-IIOP なんて (一部の例外を除き) 使用することはないと思いますが。

その他にも Java EE 7 には様々な API が用意されています。JavaFX のアプリケーションに Payara Micro を埋め込むことで、それらの API も気軽に使用することができます。

3. シャットダウン

Payara Micro を JavaFX に組み込むと、通常の Ctrl+C によるシャットダウンができません。これは JavaFX 側でシャットダウン・フックを完全に押さえており、Payara Micro のシャットダウン・コードをシャットダウン・フックに追加する余地がないためだと思われます。この制限のため、Payara Micro のシャットダウンは javafx.application.Application#stop メソッドから PayaraMicro#shutdown または PayaraMicroRuntime#shutdown メソッドを呼び出すことで実現する必要があります。

@Override
public void stop() throws Exception {
    if (runtime != null) {
        runtime.shutdown();
    }
}

なお、Payara Micro を明示的にシャットダウンしなかった場合には、それを動作させていた Java VM (プロセス) が残ったままとなりますので、注意が必要です。

4. まとめ

いくつかの制約事項はありますが、JavaFX と Payara Micro を組み合わせて使用することは可能です。javapackager を使用すれば、Java EE ランタイムを内包した JavaFX アプリケーションをネイティブ・パッケージとして配布することも可能になります。フロントエンドを JavaFX、バックエンドを Payara Micro で構成した、比較的小さな Java EE アプリケーションを開発および配布する場合には、ネイティブ・パッケージも作成できる上記の方法も選択肢のひとつとしてあげられることでしょう。

今回のサンプルコードは一応 https://github.com/khasunuma/payaramicro-on-javafx にあります。急ぎ追加した JAX-RS と WebSocket に関するコードが含まれていないため、あくまでテンプレートとしての扱いでお願いします。

明日は、Payara が「Payara が持つ 2 つのクラスタリング」、JavaFX がさくらばさん (@skrb)、Java EE がタイガーたいぞー (@tigertaizo)さんです。

この記事は、Payara Advent Calendar 2016 の 5 日目です。昨日は「Payara Micro の設計と実装」です。

Payara Micro のマニュアルには、Payara Micro は Payara Server とクラスタを組むことができるという記載があります (原文: "Payara Micro can cluster with Payara Server and share web session and JCache data.")。そこで、実際に試してみました。

なお、実施時期の都合により Payara 4.1.1.163 (旧バージョン) での検証となることをはじめにお断りしておきます。

1. 事前準備

Payara Micro は既定で Hazelcast の Auto Clustering が有効になっているため、特別な設定は必要ありません。一方、Payara Server は GlassFish との互換性維持のためか、Hazelcast が既定で無効となっているため、最初に有効化する必要があります。

Payara Server の Hazelcast を有効化するには、大きく管理コンソール (GUI) を使用する方法と、asadmin の起動時オプションで指定する方法の 2 種類があります。

1.1. 管理コンソールから Hazelcast を有効化する

管理コンソールから Hazelcast の設定を変更するには、図1 の Hazelcast 構成画面から操作します。手前味噌ですが、この画面の日本語化を担当したのは私です (Payara 4.1.153 以降)。

Hazelcast Configuration.png
figure 1.1 - Hazelcast 構成画面

スクリーンショットの赤枠で囲った部分、「Hazelcast分散キャッシュ機能を有効にするかどうかを決定します。」のチェックを ON にして、設定を保存してください。再起動は不要です。これだけで Payara Server の Hazelcast は有効になります。

もし、はじめからチェックが ON になっていた場合は、既に Hazelcast が有効になっています。

管理コンソールからだけでは本当に Hazelcast が有効化されたのか確認できないため (メッセージを信じるしかない)、念のため server.log の中身を見てみます。ここではスペースの関係上、Hazelcast に関連する場所だけ抜粋します。

[2016-12-05T23:38:15.075+0900] [Payara 4.1] [INFO] [] [com.hazelcast.instance.DefaultAddressPicker] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095075] [levelValue: 800] [[
  [LOCAL] [development] [3.6.4] Prefer IPv4 stack is true.]]

[2016-12-05T23:38:15.134+0900] [Payara 4.1] [INFO] [] [com.hazelcast.instance.DefaultAddressPicker] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095134] [levelValue: 800] [[
  [LOCAL] [development] [3.6.4] Picked Address[192.168.184.67]:5900, using socket ServerSocket[addr=/0:0:0:0:0:0:0:0,localport=5900], bind any local is true]]

[2016-12-05T23:38:15.147+0900] [Payara 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095147] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Hazelcast 3.6.4 (20160701 - 5b94d9f) starting at Address[192.168.184.67]:5900]]

[2016-12-05T23:38:15.147+0900] [Payara 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095147] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.]]

[2016-12-05T23:38:15.148+0900] [Payara 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095148] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Configured Hazelcast Serialization version : 1]]

[2016-12-05T23:38:15.281+0900] [Payara 4.1] [INFO] [] [com.hazelcast.spi.OperationService] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095281] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Backpressure is disabled]]

[2016-12-05T23:38:15.300+0900] [Payara 4.1] [INFO] [] [com.hazelcast.spi.impl.operationexecutor.classic.ClassicOperationExecutor] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095300] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Starting with 2 generic operation threads and 4 partition operation threads.]]

[2016-12-05T23:38:15.706+0900] [Payara 4.1] [INFO] [] [com.hazelcast.instance.Node] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095706] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Creating MulticastJoiner]]

[2016-12-05T23:38:15.710+0900] [Payara 4.1] [INFO] [] [com.hazelcast.core.LifecycleService] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095710] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Address[192.168.184.67]:5900 is STARTING]]

[2016-12-05T23:38:15.790+0900] [Payara 4.1] [INFO] [] [com.hazelcast.nio.tcp.nonblocking.NonBlockingIOThreadingModel] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443095790] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] TcpIpConnectionManager configured with Non Blocking IO-threading model: 3 input threads and 3 output threads]]

[2016-12-05T23:38:18.600+0900] [Payara 4.1] [INFO] [] [com.hazelcast.cluster.impl.MulticastJoiner] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098600] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] 


Members [1] {
	Member [192.168.184.67]:5900 this
}
]]

[2016-12-05T23:38:18.651+0900] [Payara 4.1] [INFO] [] [com.hazelcast.jmx.ManagementService] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098651] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Hazelcast JMX agent enabled.]]

[2016-12-05T23:38:18.679+0900] [Payara 4.1] [INFO] [] [com.hazelcast.core.LifecycleService] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098679] [levelValue: 800] [[
  [192.168.184.67]:5900 [development] [3.6.4] Address[192.168.184.67]:5900 is STARTED]]

[2016-12-05T23:38:18.691+0900] [Payara 4.1] [INFO] [] [fish.payara.nucleus.hazelcast.HazelcastCore] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098691] [levelValue: 800] [[
  Hazelcast Instance Bound to JNDI at payara/Hazelcast]]

[2016-12-05T23:38:18.692+0900] [Payara 4.1] [INFO] [] [fish.payara.nucleus.hazelcast.HazelcastCore] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098692] [levelValue: 800] [[
  JSR107 Caching Provider Bound to JNDI at payara/CachingProvider]]

[2016-12-05T23:38:18.692+0900] [Payara 4.1] [INFO] [] [fish.payara.nucleus.hazelcast.HazelcastCore] [tid: _ThreadID=136 _ThreadName=admin-thread-pool(6)] [timeMillis: 1478443098692] [levelValue: 800] [[
  JSR107 Default Cache Manager Bound to JNDI at payara/CacheManager]]

Hazelcast クラスタへ参加したことを確定付けているのは、上記ログのうち以下の部分です。Hazelcast でクラスタ・ノードの追加・削除を繰り返しているとその度に現れるメッセージですので、覚えておきましょう。

Members [1] {
	Member [192.168.184.67]:5900 this
}

ちなみに、現時点では 1 つのノードしかクラスタに参加していないため、Member は 1 つしか表示されていません。

1.2. asadmin から Hazelcast を有効化する

管理コンソールが使用できない環境では、asadmin から Hazelcast を有効化します。

asadmin set-hazelcast-configuration --enabled=true [ --dynamic=true ]

asadmin を使用する場合には、サーバーを起動するたびに設定を行う必要があるます。

1.3. domain.xml に設定を記述する

domain を停止した状態で domain.xml に以下の 1 行を追加します。

<hazelcast-runtime-configuration enabled="true"/>

2. Payara Server を起動する

Hazelcast が有効になっている状態で Payara Server を起動します。念のため、1.1. で示したようなログが server.log に出力されていることを確認してください。

3. Payara Micro を起動する

ここまでの手順に引き続き、Payara Micro を起動します。以下に起動時のログを示します。

hasunuma@pasiphae:~$ java -jar greeting\-0.0.1\-SNAPSHOT.jar                    
[2016-12-05T23:40:54.549+0900] [Payara Micro 4.1] [INFO] [NCLS-CORE-00087] [javax.enterprise.system.core] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254549] [levelValue: 800] Grizzly Framework 2.3.25 started in: 26ms - bound to [/0.0.0.0:8080]

[2016-12-05T23:40:54.640+0900] [Payara Micro 4.1] [INFO] [NCLS-CORE-00058] [javax.enterprise.system.core] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254640] [levelValue: 800] Network listener https-listener on port 8443 disabled per domain.xml

[2016-12-05T23:40:54.752+0900] [Payara Micro 4.1] [INFO] [SEC-SVCS-00100] [javax.enterprise.security.services] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254752] [levelValue: 800] Authorization Service has successfully initialized.

[2016-12-05T23:40:54.805+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.instance.DefaultAddressPicker] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254805] [levelValue: 800] [LOCAL] [development] [3.6.4] Prefer IPv4 stack is true.

[2016-12-05T23:40:54.809+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.instance.DefaultAddressPicker] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254809] [levelValue: 800] [LOCAL] [development] [3.6.4] Picked Address[192.168.184.89]:5901, using socket ServerSocket[addr=/0.0.0.0,localport=5901], bind any local is true

[2016-12-05T23:40:54.819+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254819] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Hazelcast 3.6.4 (20160701 - 5b94d9f) starting at Address[192.168.184.89]:5901

[2016-12-05T23:40:54.820+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254820] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.

[2016-12-05T23:40:54.820+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.system] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254820] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Configured Hazelcast Serialization version : 1

[2016-12-05T23:40:54.961+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.spi.OperationService] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254961] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Backpressure is disabled

[2016-12-05T23:40:54.978+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.spi.impl.operationexecutor.classic.ClassicOperationExecutor] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443254978] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Starting with 2 generic operation threads and 2 partition operation threads.

[2016-12-05T23:40:55.338+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.instance.Node] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443255338] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Creating MulticastJoiner

[2016-12-05T23:40:55.341+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.core.LifecycleService] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443255341] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Address[192.168.184.89]:5901 is STARTING

[2016-12-05T23:40:55.413+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.nio.tcp.nonblocking.NonBlockingIOThreadingModel] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443255413] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] TcpIpConnectionManager configured with Non Blocking IO-threading model: 3 input threads and 3 output threads

[2016-12-05T23:40:55.562+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.cluster.impl.MulticastJoiner] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443255562] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Trying to join to discovered node: Address[192.168.184.67]:5900

[2016-12-05T23:40:55.634+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.nio.tcp.InitConnectionTask] [tid: _ThreadID=58 _ThreadName=cached2] [timeMillis: 1478443255634] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Connecting to /192.168.184.67:5900, timeout: 0, bind-any: true

[2016-12-05T23:40:55.641+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.nio.tcp.TcpIpConnectionManager] [tid: _ThreadID=58 _ThreadName=cached2] [timeMillis: 1478443255641] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Established socket connection between /192.168.184.89:47714 and /192.168.184.67:5900

[2016-12-05T23:41:02.583+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.cluster.ClusterService] [tid: _ThreadID=38 _ThreadName=hz._hzInstance_1_development.generic-operation.thread-0] [timeMillis: 1478443262583] [levelValue: 800] [[
  [192.168.184.89]:5901 [development] [3.6.4] 

Members [2] {
        Member [192.168.184.67]:5900
        Member [192.168.184.89]:5901 this
}
]]

[2016-12-05T23:41:04.602+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.jmx.ManagementService] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264602] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Hazelcast JMX agent enabled.

[2016-12-05T23:41:04.730+0900] [Payara Micro 4.1] [INFO] [] [com.hazelcast.core.LifecycleService] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264730] [levelValue: 800] [192.168.184.89]:5901 [development] [3.6.4] Address[192.168.184.89]:5901 is STARTED

[2016-12-05T23:41:04.735+0900] [Payara Micro 4.1] [INFO] [] [fish.payara.nucleus.eventbus.EventBus] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264735] [levelValue: 800] Payara Clustered Event Bus Enabled

[2016-12-05T23:41:04.736+0900] [Payara Micro 4.1] [INFO] [] [fish.payara.nucleus.store.ClusteredStore] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264736] [levelValue: 800] Payara Clustered Store Service Enabled

[2016-12-05T23:41:04.738+0900] [Payara Micro 4.1] [INFO] [] [fish.payara.nucleus.exec.ClusterExecutionService] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264738] [levelValue: 800] Payara Clustered Exector Service Enabled

[2016-12-05T23:41:04.738+0900] [Payara Micro 4.1] [INFO] [] [fish.payara.nucleus.store.ClusteredStore] [tid: _ThreadID=1 _ThreadName=main] [timeMillis: 1478443264738] [levelValue: 800] Payara Cluster Service Enabled

Hazelcast クラスタへ参加したことを確定付けているのは、上記ログのうち以下の部分です。

Members [2] {
        Member [192.168.184.67]:5900
        Member [192.168.184.89]:5901 this
}

ちなみに、このでは 2 つ目のノードがクラスタに参加したため、Member には 2 つ表示されています。

4. 説明しなかったこと

さらなるインスタンスの追加や削除については、スペースの都合で割愛しましたが、ノード間のネットワーク接続が正常で、かつ、ここまでの手順に問題が発生していなければ正しく行われるはずです。結果は必ず Hazelcast のログである Members [n] { (ノード一覧) } で簡単に確認できるため、最低限この部分のチェックだけは怠らないようにしてください。

5. Payara 4.1.1.164 追補

Payara 4.1.1.164 では管理コンソールに Hazelcast のクラスタ・メンバを一覧できる画面が追加されました。合わせて、Hazelcast 構成タブの位置も移動になっています。

hazelcast-cluster-members.png
figure 5.1 - Hazelcast クラスタ・メンバ一覧

hazelcast-configuration.png
figure 5.2 - Hazelcast 構成

[Presentation] Payara Micro の設計と実装

この記事は Payara Advent Calendar 2016 の 4 日目です。昨日は「Payara クイック・スタート・ガイド」です。

昨日開催された JJUG CCC 2016 Fall (もう 12 月なのに Fall というのも妙ですが、諸般の事情があったのでしょう...) で同じタイトルの発表を行いました。詳しくは、以下のスライドを参照してください。

50 分という時間制限の中では Payara Micro のすべてを解説することはできないため、ブートストラップとシャットダウン、Hazelcast による自動クラスタリング、アプリケーションのデプロイ、Uber Jar の生成に内容を絞りました。いずれも Payara Micro 独自の機能であり、深く解説するには好都合なトピックでした。また、いくつかの機能については多くのアプリケーション・サーバーで共通しているものであり、アプリケーション・サーバー一般論を理解する上でも何からのヒントになったのではないかと思っています。

今回、一番伝えたかったことは、多くの Java アプリケーション開発者にとってブラックボックスとなっている Java EE アプリケーション・サーバーであっても、結局は main メソッドを持つ Java アプリケーションに過ぎないということです。Payara Micro だけでなく Tomcat、WildFly、GlassFish のブートストラップに関する情報をスライドに載せたのも、そのことを意図しています。このことさえ分かれば、Java EE アプリケーション・サーバーはもはやブラックボックスとは思えなくなります。

Hazelcast に関する説明は、一応自分なりには頑張りましたが、やはり「CLOVER を参照せよ」が最終的な解答です。かずひらさん (@kazuhira_r) には申し訳ないのですが、これからも頼らせて頂きます。

この記事は、以下の過去記事と強い関連があります。事前に目を通しておくことをお勧めします。

Payara Micro (Payara MicroProfile を含む) には、ROOT.war という名前の WAR ファイルをデプロイして UberJAR を作成すると、コンテキストルートが / になるという特別な機能があります (現状、WAR セパレートの場合は WAR ファイルの既定値が用いられますが、こちらも UberJAR と同じ仕様となるように変更される見込みです)。呼び出し時にコンテキストルートを考慮しなくて良くなるため、使い始めるととても便利です。

早速ですが、作成する WAR ファイル名を ROOT.war に固定して UberJAR 化する pom.xml を作成してみました。groupId と artifactId を任意の値に設定して、ご活用ください。

<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>hello</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>fish.payara.extras</groupId>
      <artifactId>payara-micro</artifactId>
      <version>4.1.1.163</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
          <warName>ROOT</warName>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.5.0</version>
        <dependencies>
          <dependency>
            <groupId>fish.payara.extras</groupId>
            <artifactId>payara-micro</artifactId>
            <version>4.1.1.163</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>payara-uber-jar</id>
            <phase>package</phase>
            <goals>
              <goal>java</goal>
            </goals>
            <configuration>
              <mainClass>fish.payara.micro.PayaraMicro</mainClass>
              <arguments>
                <argument>--deploy</argument>
                <argument>${basedir}/target/ROOT.war</argument>
                <argument>--outputUberJar</argument>
                <argument>${basedir}/target/${project.build.finalName}.jar</argument>
              </arguments>
              <includeProjectDependencies>false</includeProjectDependencies>
              <includePluginDependencies>true</includePluginDependencies>
              <executableDependency>
                <groupId>fish.payara.extras</groupId>
                <artifactId>payara-micro</artifactId>
              </executableDependency>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Payara MicroProfile を使用する際は、artifactId=payara-microprofile、version=1.0 に変更します。また、Eclipse WTP 標準レイアウトに対応させる場合は maven-war-plugin の configuration 要素に WebContent を追記します。

情報が散乱気味だったため、簡潔ですが整理してみました。コンテキストルート周りで苦しめられている方々の一助になれば幸いです。

Payara Micro のコンテキストルート

Java EE サーバー上に Web アプリケーションをデプロイする際、避けて通れないものが「コンテキストルート」の存在です。Java EE サーバーは、コンテキストルートを基準にどの Web アプリケーションにリクエストを割り当てるかを決定します。

Payara Micro でもコンテキストルートは関わってきます。「マイクロサービスのランタイムなのにコンテキストルート?」とお思いの方も多いでしょうが、Payara Micro はそもそも埋め込み GlassFish サーバーのラッパーからスタートしたものであるため、コンテキストルートの存在は付いて回ります。

1. Payara Micro の不可解な挙動

前回、Payara Microprofile のスケルトン・プロジェクトについてご紹介しました。コード自体は非常に簡単なものなのですが、実行する際に思わぬ落とし穴にハマります。まず、前回のスケルトン・プロジェクトで作成した WAR ファイル名が pom.xml の記述通り greeting-0.0.1-SNAPSHOT.war であるものと仮定して、まずは Maven のビルドに従って UberJar 化したものを実行してみましょう (なお、コンテキストルートは glassfish-web.xml で /greeting としています)。

java -jar greeting-0.0.1-SNAPSHOT.jar

では、任意の HTTP クライアント (Web ブラウザでも可) で、リクエスト http://localhost:8080/greeting/say を送信してみてください。どうなりましたか?

残念なことに、HTTP 404 が返ってきます。どうしてこうなるのでしょう? UberJAR をビルドする pom.xml にミスでもあったのでしょうか?

仮に pom.xml にミスがあったとして、手動で UberJAR を作成してみましょう。

java -jar payara-microprofile-1.0.jar --deploy greeting-0.0.1-SNAPSHOT.war --outputUberJar greeting-0.0.1-SNAPSHOT.jar
java -jar greeting-0.0.1-SNAPSHOT.jar

リトライしてみますが、結果は同じく HTTP 404 です。いったい何が悪いのか?

基本に立ち戻って、UberJAR にせず WAR ファイルをセパレートで実行してみたらどうなるでしょう?

java -jar payara-microprofile-1.0.jar --deploy greeting-0.0.1-SNAPSHOT.war

実は、これだと HTTP 200 が返ります。つまり正常です。参考までに、greeting-0.0.1-SNAPSHOT.war を Payara Server にデプロイして実行してもお同じように HTTP 200 が返ります。

迷宮入りしそうだったので、最初に立ち戻って、コンソールログを観察することにしました。すると...

コンテキストルートが /greeting ではなく /greeting-0.0.1-SNAPSHOT (WAR ファイル名) になっていたのです。早速、UberJAR で起動して、リクエスト http://localhost:8080/greeting-0.0.1-SNAPSHOT/say を送信したところ、無事 HTTP 200 が返ってきました。

2. Payara Micro のデプロイの仕組み

UberJAR の実現方法にはいくつかの方法があります。Payara Micro の UberJARは少し特殊で、まず --deploy オプションで Payara Micro 自身の体内に WAR ファイルを取り込み、その状態のイメージをそのまま --outputUberJar オプションを使って書き出す仕組みになっているのです。実際に、UberJar 化した Payara Micro の中には、そっくりそのまま WAR ファイルが入っています。より正確には UberJar 内部の /META-INF/deploy に WAR ファイルが埋め込まれています (これは jar や unzip で展開すればすぐに分かります)。

Payara Micro の API には WAR をデプロイするものが複数あり、WAR ファイルをセパレートで実行する場合には File クラスを経由して WAR ファイルをロードするものを使用します。その他にも任意の InputStream クラスから WAR ファイルをロードするものもあって、Payara Micro の UberJAR は後者を使用しているのです。より詳細に書くと、クラスローダーから UberJAR 内の WAR ファイルを InputStream で取り出して (Class.getResourceAsStream() を使う)、それをデプロイヤに渡しています。なお、この機能は埋め込み GlassFish API をそのまま使用しているものなので、Payara Micro 誕生前から存在しています。

さて、今回問題だったのは、WAR ファイルをセパレートで実行した場合には glassfish-web.xml のコンテキストルート設定を認識し、一方で UberJAR で実行した場合にはそれが認識されなかった点にあります。

この挙動の相違は、PayaraMicro クラスの deployAll メソッド実装を探ると明確になります。

WAR ファイルをセパレートで実行する場合は、deployAll メソッドのおよそ 1485 行目あたりからの処理が実行されます。

if (deployments != null) {
  for (File war : deployments) {
    if (war.exists() && war.canRead()) {
      deployer.deploy(war, "--availabilityenabled=true", "--force=true");
      deploymentCount++;
    } else {
      logger.log(Level.WARNING, "{0} is not a valid deployment", war.getAbsolutePath());
    }
  }
}

ここは埋め込み GlassFish API でデプロイを司る Deployer クラスを用いて WAR ファイルを (--deploy オプションで指定された数だけ) 順次デプロイしている箇所なのですが、デプロイオプションとして --availabilityenabled=true と --force=true を指定しています。前者は HA 構成の有効化、後者は強制デプロイをそれぞれ意味します。実はこの 2 つのオプションは本題とは関係ありません。これらのオプションだけで WAR ファイルをデプロイした場合、コンテキストルートはアプリケーションサーバーの既定値となります。もし WAR ファイルに glassfish-web.xml が含まれ、かつコンテキストルートが設定されていた場合には、その設定値が Web アプリケーションのコンテキストルートとして割り当てられます。

一方で UberJAR の場合は、deployAll メソッドのおよそ 1534 行目あたりからの処理が実行されます。

URL url = this.getClass().getClassLoader().getResource("META-INF/deploy");
if (url != null) {
  String entryName = "";
  try {
    HashSet entriesToDeploy = new HashSet<>();
    JarURLConnection urlcon = (JarURLConnection) url.openConnection();
    JarFile jFile = urlcon.getJarFile();
    Enumeration entries = jFile.entries();
    while (entries.hasMoreElements()) {
      JarEntry entry = entries.nextElement();
      entryName = entry.getName();
      if (!entry.isDirectory() && !entry.getName().endsWith(".properties") && !entry.getName().endsWith(".xml") && entry.getName().startsWith("META-INF/deploy")) {
        entriesToDeploy.add(entry.getName());
      }
    }

    for (String entry : entriesToDeploy) {
      File file = new File(entry);
      String contextRoot = file.getName();
      if (contextRoot.endsWith(".ear") || contextRoot.endsWith(".war") || contextRoot.endsWith(".jar") || contextRoot.endsWith(".rar")) {
        contextRoot = contextRoot.substring(0, contextRoot.length() - 4);
      }

      if (contextRoot.equals("ROOT")) {
        contextRoot = "/";
      }

      deployer.deploy(this.getClass().getClassLoader().getResourceAsStream(entry), "--availabilityenabled",
              "true", "--contextroot",
              contextRoot, "--name", file.getName(), "--force=true");
      deploymentCount++;
    }
  } catch (IOException ioe) {
    logger.log(Level.WARNING, "Could not deploy jar entry {0}",
            entryName);
  }
} else {
  logger.info("No META-INF/deploy directory");
}

こちらも Deployer クラスを用いて WAR ファイルを順次デプロイするのですが、デプロイオプションとして --availabilityenabled=true と --force=true に加え --contextroot が加わっています。その前後を見ると分かりますが、コンテキストルートの設定値は WAR ファイル名 (厳密には拡張子を除いたもの) となっています。--contextroot オプションは glassfish-web.xml の指定よりも優先されるため、UberJAR のコンテキストルートは WAR ファイル名で固定されてしまうです。

3. まとめ

Payara および Payara Micro (Payara Microservice を含む) のコンテキストルートの決定には癖があります。

ランタイムコンテキストルートの決定
Payara Server 既定値、glassfish-web.xml に指定があれば優先
Payara Micro (WAR セパレート) 既定値、glassfish-web.xml に指定があれば優先
Payara Micro (UberJAR) WAR ファイル名、glassfish-web.xml の指定は無視

Payara Micro は Payara 4.1.152 から導入されました。その当時はコンテキストルートの決定について明確な方針がなかったのかもしれません。一方で、Payara Micro の UberJAR 対応は Payara 4.1.1.162 以降と比較的新しく、マイクロサービス・アーキテクチャを意識してコンテキストルートの決定ルールを変更した可能性があります。とはいえ、後方互換性を維持するために当初から存在したデプロイはそのまま残すのが妥当で、新しく追加した UberJAR 用のデプロイのみ新しいルールを適用したと考えるのが一番現実的な路線ではないかと思われます。


2016-10-28 : 訂正および補足

一方で UberJAR の場合は、deployAll メソッドのおよそ 1534 行目あたりからの処理が実行されます。

ソース・ファイルに見落としがありました。UberJAR の場合、デプロイ対象が ROOT.war であれば、コンテキストルートは / に設定されます (ROOT.ear、ROOT.jar、ROOT.rar も同様です)。それ以外の場合は WAR ファイル名が使用されます。

現状では、WAR セパレートの場合のコンテキストルートは上記の通り WAR の既定値となりますが、こちらについても ROOT.war の場合にコンテキストルートが / に設定されるよう修正される予定です。

Payara MicroProfile 1.0

今回は先月リリースされた Payara MicroProfile 1.0 についてご紹介します。Payara MicroProfile は Payara Server の MicroProfile 実装で、Payara Micro をベースに作られています。Payara MicroProfile については Director の Steve Millidge による紹介記事がありますので、併せてご覧ください。

1. MicroProfile とは?

MicroProfile は Java EE 技術をベースとしたマイクロサービス向けのプロファイルで、MicroProfile.io によって策定されています。

MicroProfile.io は IBMLondon Java CommunityRed HatTomitribePayara および SouJava といったベンダーおよびユーザーグループで構成されており、現在のところ Java Community Process とは独立して活動しています (Eclipse Foundation に提案しようという動きもあるようです)。

2016 年 9 月に最初のバージョンがリリースされ、現時点では WildFly Swarm と Payara MicroProfile がこれに対応しています。MicroProfile 1.0 に含まれる API は JAX-RS、CDI および JSON-P という最小セットになっていますが、今後必要な API を順次追加する見込みです。Web Profile が Java EE Full Platform の完全なサブセットであるのに対して、MicroProfile は (現時点ではサブセットになっていますが) 必ずしもサブセットにはこだわらない姿勢を見せています。

2. Payara MicroProfile とは?

Payara MicroProfile は Payara Micro をベースに開発された MicroProfile のランタイムです。サイズは 32.4 MB と Payara Micro よりもさらに小さくなっています。内包している API を除いて Payara Micro と同じコードを使用しているため、Payara Micro が持つ各種オプションをそのまま使用することができます。Hazelcast によるクラスタリングと JCache 実装もそのまま提供されます。

Payara Micro の導入に関しては、少し情報は古いですが (Uber JAR 非対応の頃)、以下のスライドを参照してください。

Payara Micro と Payara MicroProfile の API を比較してみましょう。

Payara Micro 4.1.1.163

  • Java EE 7 Web Profile (Servlet, JSTL, EL, JSP, WebSockets, JSF, JAX-RS, EJB lite, JTA, JPA, Bean Validation, CDI, Interceptors)
  • Concurrency
  • JBatch
  • JCache (by Hazelcast)

Payara MicroProfile 1.0

  • JAX-RS
  • CDI
  • JSON-P
  • JCache (by Hazelcast)

Payara MicroProfile 1.0 は MicroProfile 1.0 仕様には含まれない JCache を内包していますが、これは Hazelcast を搭載していることによります。

MicroProfile 1.0 には既にいくつかのサンプルが用意されています。これらは WildFly Swarm 向けに実装されたものですが、Payara MicroProfile や Apache TomEE (TomEE もサポートを提供する Tomitribe が MicroProfile 対応を表明しています) でも動作します。

補足: Payara Micro のバージョンは母体となった Payara Server のバージョンに沿っていますが、Payara MicroProfile のバージョンは MicroProfile 仕様のバージョンに合わせています。

3. Payara MicroProfile の pom.xml テンプレート

Payara MicroProfile を使用する際の pom.xml 記述は基本的に Payara Micro と同じです。現在の Payara Micro は Uber JAR に対応しているため、pom.xml にも Uber JAR 作成用の記述を用意しておくと便利です。

<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>hello</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.5.0</version>
        <dependencies>
          <dependency>
            <groupId>fish.payara.extras</groupId>
            <artifactId>payara-microprofile</artifactId>
            <version>1.0</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <id>payara-uber-jar</id>
            <phase>package</phase>
            <goals>
              <goal>java</goal>
            </goals>
            <configuration>
              <mainClass>fish.payara.micro.PayaraMicro</mainClass>
              <arguments>
                <argument>--deploy</argument>
                <argument>${basedir}/target/${project.build.finalName}.war</argument>
                <argument>--outputUberJar</argument>
                <argument>${basedir}/target/${project.build.finalName}.jar</argument>
              </arguments>
              <includeProjectDependencies>false</includeProjectDependencies>
              <includePluginDependencies>true</includePluginDependencies>
              <executableDependency>
                <groupId>fish.payara.extras</groupId>
                <artifactId>payara-microprofile</artifactId>
              </executableDependency>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Payara MicroProfile の groupId は fish.payara.extras (Payara Micro と共通)、artifactId は payara-microprofile (Payara Micro の場合は payara-micro) となります。また、バージョンは 1.0 を指定します。

上記の pom.xml は Steve の記事にあるものと概ね同じです。exec-maven-plugin を使用して Uber JAR 作成を行っているところが肝になります。Maven の package フェーズで WAR ファイルが作成された後に Uber JAR が作成されます。

私は Eclipse を多用している関係で、Eclipse WTP プロジェクトを Maven プロジェクトに変換することが多くあります。私の場合は、プロジェクト形式の変換で生成された pom.xml に exec-maven-plugin の記述を追記して、Uber JAR 作成に対応させています。


<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.exsample</groupId>
  <artifactId>hello</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.5.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.6</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
<!-- ここから追記 --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.5.0</version> <dependencies> <dependency> <groupId>fish.payara.extras</groupId> <artifactId>payara-microprofile</artifactId> <version>1.0</version> </dependency> </dependencies> <executions> <execution> <id>payara-uber-jar</id> <phase>package</phase> <goals> <goal>java</goal> </goals> <configuration> <mainClass>fish.payara.micro.PayaraMicro</mainClass> <arguments> <argument>--deploy</argument> <argument>${basedir}/target/${project.build.finalName}.war</argument> <argument>--outputUberJar</argument> <argument>${basedir}/target/${project.build.finalName}.jar</argument> </arguments> <includeProjectDependencies>false</includeProjectDependencies> <includePluginDependencies>true</includePluginDependencies> <executableDependency> <groupId>fish.payara.extras</groupId> <artifactId>payara-microprofile</artifactId> </executableDependency> </configuration> </execution> </executions> </plugin>
<!-- ここまで追記 --> </plugins> </build> </project>

私自身、Payara MicroProfile を使い始めて間もないのですが、Payara Micro の使用経験があれば API の差を除いて戸惑うことはないと思います。もし JPA が必要ならば EclipseLink を依存関係に追加すれば良いですし、画面周りのフレームワークが必要ならば Ozark を加える方法もあります。

MicroProfile 1.0 API の品揃えの関係から、CDI に関して相応の知識が要求されるかもしれません。幸い、書籍「パーフェクト Java EE」にまとまった CDI の解説があるため、手元に用意しておくと役に立つかもしれません。

4. まとめ

MicroProfile は生まれたばかりの仕様です。これからどのように発展していくのかわかりませんが、立ち上がりの今だからこそ私達に貢献できる範囲が非常に広いとも言えるでしょう。Payara でもマイクロサービス (Payara Micro) と MicroProfile を積極的に推進していますので、これからの展開にご期待ください。併せて、Payara への貢献もよろしくお願いします。

Payara Micro のからくり

この記事は Java EE Advent Calendar 2015 の 8 日目です。昨日は @n_agetsu さんの「Javaバッチ処理のNFS向けファイルI/O」でした。

今回は当初、GlassFish と Payara の最新事情に関する記事を予定していましたが、GlassFish Japan に掲載した方が良さそうなので、後日そちらに書きます。代わりに今回は Payara Micro のあまり知られていないことを紹介します。

1. Payara Micro の正体

Payara Micro の正体は埋め込み GlassFish のラッパーです。その証拠に GlassFish の埋め込みサーバー API がそのまま使えます。個人的には Payara Micro の API の方がシンプルで使いやすいと思いますが。

Payara Micro は非常に短期間で開発され、しかも当初から商用レベルの品質に達していました。埋め込み GlassFish という完成品をラップしただけなので、ある意味当然の結果と言えます。

2. Payara Micro を Ctrl + C で停止できる理由

埋め込み GlassFish は本来、シャットダウンのためのコードを記述する必要があります(忘れるとリソースリークの原因となります)。Payara Micro はシャットダウン・フックに埋め込み GlassFish のシャットダウン・コードを実装しているため、Ctrl + C で自動的にシャットダウンするような仕組みになっています。

ただし、Payara Micro API を JavaFX アプリケーションから使用する場合、どうやら JavaFX がシャットダウン・フックを乗っ取ってしまうらしく、別途シャットダウンのメソッドを呼び出す必要があります。

3. Payara Micro で asadmin のコマンドを実行できる理由

asadmin は見かけこそスクリプトですが、実体は GlassFish/Payara の管理・監視 API をラップする一番外側の皮です。つまり asadmin を使わなくても API を直接呼び出すことで同様の操作ができるわけです。例えば Eclipse の GlassFish Tool などは直接 API を呼び出すことで GlassFish/Payara の起動・停止・デバッグ実行を実現しています。

Payara Micro API も GlassFish の管理・監視 API を呼び出すことで asadmin コマンドを実行できるようになっています。そもそも埋め込み GlassFish 自身が管理・監視 API を内包していて、埋め込みサーバー API のバックエンドに利用しているのです。

ただし、Payara Micro API でドメインの起動・停止・再起動はやらない方が無難です。少なくとも筆者の環境では上手くいった試しがありません。

4. Payara Micro の自動バインドとは?

Payara Micro には動的に HTTP(S) ポートをリスンする自動バインド機能が備わっています(既定では無効)。これは指定したリスンポート (既定では HTTP が 8080、HTTPS が8181) が空いているかどうかを調べ、使用中ならば空いているポートが見つかるまでリスンポートをインクリメントしていく仕掛けになっています。この機能は Hazelcast のリスンポート周りの挙動を HTTP(S) でも実現したいという、ただそれだけの理由で追加されました。実装も難しいものではありません。

自動バインドは機能そのものとしては面白いしデモのネタにはなるのですが、現在のロードバランサーの多くがアドレスとポート番号を直接指定していることを考えると、今のところ実用性は薄いのかもしれません。

今回は Payara Micro が裏で何をしているのかについて、いくつかピックアップしてご紹介しました。Payara はオープンソースなので、ソースコードを見れば今回ご紹介したことは全部分かってしまうのですが、ミドルウェアのソースコードを積極的に見ることはあまりないかと思って、ちょっとまとめてみました。

明日は @koduki さんです。

11 月 28 日に開催された JJUG CCC 2015 Fall で Payara Micro のお話をしてきました。まずは発表資料から。

今回、補足事項はあまりないのですが、API の PayaraMicro.bootStrap() の戻り値は 4.1.153 から PayaraMicroRuntime になりました。それ以前は void だったので API でも起動後の状態変更はできませんでした。

リハーサルの段階でセッションが 35 分で収まることが分かって、残りの時間は長めの質問タイムにするか、デモにするか、会場の皆さんの意見を聞いて決めることにしていました。まあ、会場の圧倒的多数がデモを選ぶだろうことは容易に予想が付いたのですが、まさか全員デモを選ぶとは...。会場の VGA が 1 系統と見越して)、iPad から Surface Pro 2 への切り替えでもたつく前提で 1 つだけデモを用意していました。ところが本編がまさかの30分以内で収まり、VGA の切り替えもすぐにできてしまったので時間が大幅に余り、予定になかった Hazelcast でのクラスタリングと自動バインド機能をご覧いただきました。即興でデモを追加するのは、非常に心臓に悪いです...。

最後に 5 分ほど質問タイムができたので、そのおさらいです。

  • Payara Micro は、埋め込み GlassFish をラップして、マイクロサービス向けに特化した実行環境です。基本的には開発環境ですべて設定を済ませ、変更が生じるときは新しいインスタンスを作成する運用になります。そうでない運用では通常の Payara Server を選択するのが良いです。補足ですが、Payara は大雑把に言うと有償サポート付きの GlassFish なので、バグの修正が早いことを除けば実運用で大きな違いは出ないと考えられます。
  • Payara Micro の JDBC データソース設定は、主にアプリケーション側で行うことになります。具体的には glassfish-web.xml と glassfish-resources.xml への記述となります。代替手段として、データソース設定済みの config.xml を用意できればオプションで差し替えて使用することも可能です(手間はかかります)。API 使用時は asadmin のサブコマンドを呼び出す方法も採れます。
  • 自動バインドは --port / --sslPort で指定したポートから順番にチェックして空いているポートで待ち受けをする仕組みなので、Payara Micro を次々と起動すれば順番にポートを埋めていきます。この機能は Hazelcast の待ち受けポート割り当てを模して作られたもので、「あったらいいな」的なものを実装しただけです。現在のロードバランサーは静的に設定されたポートでリクエストを振り分けるものが多く、今のところ自動バインド機能の実効性については未知数です。

今回の JJUG CCC は、あまりにも体調が悪くて発表がなければ休んでいたくらいなので、うらがみさんのセッションを聴いて、自分のをやって、資料の公開を確認してから撤収しました。そういう運命だったと思って、今回は諦めました。

Payara Micro を試してみる

GlassFish ベースのアプリケーション・サーバーである Payara は、単なるバグフィックス版ではなく、Java EE のリファレンス実装という縛りを受けないがゆえ、オリジナルにはない追加機能も搭載されています。現在リリースされている Payara 4.1.151 では Hazelcast を同梱し JCache (JSR 107) を先行実装しています。次期リリース 4.1.152 から追加される第 2 の追加機能が、今回ご紹介する Payara Micro と呼ばれるものです。Payara Blog にも紹介エントリがあります。

Payara Micro はマイクロサービス向けの Payara ビルドです。Web アプリケーションを作成し (ここでは test.war とします)、それをコマンドライン引数に指定するだけで、GlassFish/Payara ベースのマイクロサービスが構築できます。

java -jar payara-micro.jar --deploy test.war

Payara Micro では、Java EE 7 Web Profile 相当の API に加え、Concurrency、JBatch および JCache (Hazelcast) をサポートします。Payara Micro の本体サイズは 54.9 MBytes で、Payara Embedded (Full Platform: 89.0 MBytes、Web Profile: 56.5 MBytes) よりもさらにコンパクトになっています。

Payara Blog では 57 MBytes とありますが、これは 1 MByte = 1,000,000 Bytes で換算したものです。

Payara Micro はライブラリとしても使用することが出来ます。上記の例をプログラムで記述すると以下のようになります。

import fish.payara.micro.BootstrapException;
import fish.payara.micro.PayaraMicro;

public class EmbeddedPayara {
  public static void main(String[] args) throws BootstrapException {
    PayaraMicro.getInstance().addDeployment("test.war").bootStrap();
  }
}

CLI、ライブラリとも、キーとなっているのは fish.payara.micro.PayaraMicro クラスです。PayaraMicro クラスの正体は GlassFish の埋め込みサーバ API のラッパーであり、さらにシャットダウン・フックで埋め込みサーバを停止するように実装しています。埋め込み GlassFish の使い方の手本としても良いと思います。

上記 2 例とも、基準となる URL は http://localhost:8080/test/ となります (HTTP ポートの既定値は 8080 です)。

最後に、Payara Micro のコマンドライン引数についてまとめておきます。

Payara Micro のコマンドライン引数
引数説明
--noCluster Hazelcast クラスタを無効にする (既定では有効)
--port <number> HTTP リスナのポート番号を設定する (既定値は 8080)
--sslPort <number> HTTP over TLS (HTTPS) リスナのポート番号を設定する (既定値は 8181)
--mcAddress <address> Hazelcast クラスタのマルチキャスト・グループ・アドレスを設定する
--mcPort <number> Hazelcast クラスタのマルチキャスト・ポートを設定する
--startPort <number> Hazelcast クラスタの開始ポート番号を設定する
--name <instance-name> Payara のインスタンス名を設定する
--rootDir <path-to-directory> com.sun.aas.instanceRoot ディレクトリ (既定値以外) を指定する。
--deploymentDir <path-to-directory> 指定したディレクトリにあるすべての .war ファイルをデプロイする
--deploy <path-to-file> 指定した .war ファイルをデプロイする
--domainConfig <path-to-file> 既存の domain.xml ファイルを指定する
--minHttpThreads <number> HTTP スレッド・プールの最小値を設定する
--maxHttpThreads <number> HTTP スレッド・プールの最大値を設定する
--help ヘルプ

Payara は Hazelcast を同梱しているため、Hazelcast のインメモリ・グリッドを利用したクラスタが標準で有効になっています。あくまで Hazelcast の機能であり GlassFish が提供するクラスタ機能ではないことに注意してください。