Liberty のテスト接続機能を利用する

## はじめに
今回は、Liberty のテスト接続機能についてまとめてみました。テスト接続機能を使用すると、アプリケーションがなくても、データベースや JMS プロバイダーへの接続を確認できます。

最初にテスト接続機能が Liberty に追加されたのは 2019年で、REST API 形式のものでした。

その後、2023年には Admin Center にもテスト接続機能が追加され、GUI 操作でテスト接続を行えるようになっています。Admin Center のテスト接続機能は、REST API 形式のテスト接続機能を内部で利用しているようです。

今回は、REST API 形式のテスト接続機能に関してまとめてみました。
Admin Center のテスト接続機能に関しては、下記の２番目のドキュメントを参照してください。

* 参照：[Open Liberty Blog: Testing database connections in Open Liberty apps with REST APIs](https://openliberty.io/blog/2019/09/13/testing-database-connections-REST-APIs.html)
* 参照：[Open Liberty 23.0.0.2でのデータベース接続のテストとサーバー停止タイムアウトの設定](https://openliberty.io/ja/blog/2023/03/26/23.0.0.2.html)


## 準備
REST API 形式のテスト接続機能を利用するには、以下の２つの準備が必要となります。

1. `restConnector-2.0` フィーチャーを有効にする
1. `administrator` ロールを持つユーザーをレジストリーに登録する

ユーザーの定義は、次のように `quickStartSecurity` タグで簡単に定義するか、または、さらにその下のように、 `basicRegistry` タグと `administrator-role` タグで定義できます。
以下の例ではパスワードを難読化していませんが、`securityUtility` コマンドで難読化して指定することをお勧めします。

```xml:serve.xml の定義例（１）
    <featureManager>
        <feature>restConnector-2.0</feature>
    </featureManager>
    <quickStartSecurity userName="admin" userPassword="pswd"/>
```

```xml:serve.xml の定義例（２）
    <featureManager>
        <feature>restConnector-2.0</feature>
    </featureManager>
    <basicRegistry id="basic" realm="WebRealm">
        <user name="admin" password="pswd" />
    </basicRegistry>
    <administrator-role>
        <user>admin</user>
    </administrator-role>
```

## データーベース接続のテスト
下記の URL にアクセスすることで、テスト接続を実行できます。

- **https://(ホスト名):(ポート番号)/ibm/api/validation/dataSource/(uid)**

`(uid)` は、`dataSource` タグの id 属性に指定されている値です。
尚、ベーシック認証で保護されていますので、前述の方法で定義した `administrator` ロールを持つユーザーでアクセスする必要があります。

データソースでの認証情報の指定方法によって、URL にアクセスする際のクエリー・パラメーターなどが変わりますので、以下に実行例とともに詳細を記載します。

### 認証情報を properties.xxxx タグで指定している場合
`properties.xxxx` タグの `user`/`password` 属性で認証情報を指定している場合は、パラメーターを指定せずに、`administrator` ロールを持つユーザーでアクセスするだけでテスト接続が実行できます。

```xml:dataSource 定義例
    <dataSource id="SampleDS" jndiName="jdbc/sample"
                type="javax.sql.XADataSource" validationTimeout="5"
                jdbcDriverRef="SampleJD">
        <properties.db2.jcc user="db2inst1" password="????" databaseName="sample" portNumber="50000" serverName="????"/>
    </dataSource>
```
接続に成功すると、`"successful": true` を含む応答が返ります。

```sh:実行例：データソースのテスト接続
$ curl --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/dataSource/SampleDS
{
   "uid": "SampleDS",
   "id": "SampleDS",
   "jndiName": "jdbc/sample",
   "successful": true,
   "info": {
      "databaseProductName": "DB2/AIX64",
      "databaseProductVersion": "SQL11059",
      "jdbcDriverName": "IBM Data Server Driver for JDBC and SQLJ",
      "jdbcDriverVersion": "4.19.26",
      "schema": "DB2INST1",
      "user": "db2inst1"
   }
}
```

接続に失敗した場合は、エラー・メッセージ `message` とスタック・トレース `stack` を含む応答が返ります。
`message` はエスケープされているので、そのままでは読めないことがありますが、jq 等で成形すると読めるようになります。

```sh:失敗例：データソースのテスト接続
$ curl --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/dataSource/SampleDS
{
   "uid": "SampleDS",
   "id": "SampleDS",
   "jndiName": "jdbc/sample",
   "successful": false,
   "failure": {
      "sqlState": "28000",
      "errorCode": "-4214",
      "class": "java.sql.SQLInvalidAuthorizationSpecException",
      "message": "[jcc][t4][2013][11249][4.19.26] \u63a5\u7d9a\u306e\u8a31\u53ef\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002  \u7406\u7531: \u30e6\u30fc\u30b6\u30fc ID \u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u304c\u7121\u52b9\u3067\u3059\u3002 ERRORCODE=-4214, SQLSTATE=28000 DSRA0010E: SQL \u72b6\u614b = 28000\u3001\u30a8\u30e9\u30fc\u30fb\u30b3\u30fc\u30c9 = -4,214",
      "stack": [
         "com.ibm.db2.jcc.am.kd.a(Unknown Source)",
         … … 省略 … …
         "java.base/java.lang.Thread.run(Thread.java:857)"
      ]
   }
}
$ curl -s --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/dataSource/SampleDS | jq
{
  "uid": "SampleDS",
  "id": "SampleDS",
  "jndiName": "jdbc/sample",
  "successful": false,
  "failure": {
    "sqlState": "28000",
    "errorCode": "-4214",
    "class": "java.sql.SQLInvalidAuthorizationSpecException",
    "message": "[jcc][t4][2013][11249][4.19.26] 接続の許可が失敗しました。  理由: ユーザー ID またはパスワードが無効です。 ERRORCODE=-4214, SQLSTATE=28000 DSRA0010E: SQL 状態 = 28000、エラー・コード = -4,214",
    "stack": [
      "com.ibm.db2.jcc.am.kd.a(Unknown Source)",
         … … 省略 … …
         "java.base/java.lang.Thread.run(Thread.java:857)"
      ]
   }
}
```

### コンテナ認証を使用している場合
`authData` で認証情報を指定している場合は、クエリー・パラメーターに `auth=container` を指定して、`administrator` ロールを持つユーザーでアクセスする必要があります。

```xml:dataSource 定義例
    <authData id="SampleAD" password="db2inst1" user="????"/>
    <dataSource id="SampleDS" jndiName="jdbc/sample"
                type="javax.sql.XADataSource" validationTimeout="5"
                containerAuthDataRef="SampleAD" jdbcDriverRef="SampleJD">
        <properties.db2.jcc databaseName="sample" portNumber="50000" serverName="????"/>
    </dataSource>
```

```sh:実行例：データソースのテスト接続
$ curl -s --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/dataSource/SampleDS?auth=container | jq
{
  … … 省略 … …
}
```

### 認証情報を定義していない場合
殆どないケースと思いますが、アプリ側で認証情報を指定していて、データソースで認証情報を指定していない場合などは、テスト接続で使用する認証情報を `X-Validation-User` ヘッダーと `X-Validation-Password` ヘッダーで指定することができます。

```sh:実行例：データソースのテスト接続
$ curl -s --insecure -u admin:pswd -H "X-Validation-User: db2inst1" -H "X-Validation-Password: ????" https://localhost:9443/ibm/api/validation/dataSource/SampleDS | jq
{
  … … 省略 … …
}
```

## 全てのデータソースを一度にテストする
データソース単位でテスト接続を行う方法を説明してきましたが、下記の URL にアクセスすると、全てのデータソースを一度にテストできます。

- **https://(ホスト名):(ポート番号)/ibm/api/validation/dataSource**

以下の実行例では、server.xml に定義されている２つのデータソースに対してテスト接続が実行され、いずれも成功していることになります。
データソース `DefaultDataSource` のテスト接続が失敗していますが、これは、Liberty 内に予め定義されているものなので、使用していないのであれば無視して支障ありません。

```sh:実行例：全てのデータソースを一度にテスト
$ curl -s --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/dataSource?auth=container | jq
[
  {
    "uid": "DefaultDataSource",
    "id": "DefaultDataSource",
    "successful": false,
    "failure": {
      "message": "CWWKO1551E: 1 つ以上の依存関係が満たされていないか、リソースを有効にするフィーチャーが使用可能ではないか、このタイプのリソースの検証が不可能です。"
    }
  },
  {
    "uid": "SampleDS",
    "id": "SampleDS",
    "jndiName": "jdbc/sample",
    "successful": true,
    "info": {
      "databaseProductName": "DB2/AIX64",
      "databaseProductVersion": "SQL11059",
      "jdbcDriverName": "IBM Data Server Driver for JDBC and SQLJ",
      "jdbcDriverVersion": "4.19.26",
      "schema": "DB2INST1",
      "user": "db2inst1"
    }
  },
  {
    "uid": "TestDS",
    "id": "TestDS",
    "jndiName": "jdbc/test",
    "successful": true,
    "info": {
      "databaseProductName": "DB2/AIX64",
      "databaseProductVersion": "SQL11059",
      "jdbcDriverName": "IBM Data Server Driver for JDBC and SQLJ",
      "jdbcDriverVersion": "4.19.26",
      "schema": "DB2INST1",
      "user": "db2inst1"
    }
  }
]
```

## JMS 接続のテスト
JMS のテスト接続を実行する場合は、接続ファクトリーの定義方法に応じて、以下の何れかの URL を使用します。

- **https://(ホスト名):(ポート番号)/ibm/api/validation/jmsConnectionFactory/(uid)**
- **https://(ホスト名):(ポート番号)/ibm/api/validation/jmsQueueConnectionFactory/(uid)**
- **https://(ホスト名):(ポート番号)/ibm/api/validation/jmsTopicConnectionFactory/(uid)**

```sh:実行例：JMS 接続ファクトリーのテスト接続
$ curl --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/jmsConnectionFactory/jms_cf_qmtest?auth=container
{
   "uid": "jms_cf_qmtest",
   "id": "jms_cf_qmtest",
   "jndiName": "jms/CF_QM_TEST",
   "successful": true,
   "info": {
      "jmsProviderName": "IBM MQ JMS Provider",
      "jmsProviderVersion": "8.0.0.0",
      "jmsProviderSpecVersion": "2.0"
   }
}
```

データソースの場合と同様に、`(uid)` を省略すると、該当タイプの接続ファクトリーのテスト接続をまとめて実行できます。`Default????ConnectionFactory` のテスト接続がエラーになりますが、使用していないなら無視して支障ありません。

```sh:実行例：全ての JMS 接続ファクトリーを一度にテスト
curl -s --insecure -u admin:pswd https://localhost:9443/ibm/api/validation/jmsConnectionFactory?auth=container | jq
[
  {
    "uid": "DefaultJMSConnectionFactory",
    "id": "DefaultJMSConnectionFactory",
    "successful": false,
    "failure": {
      "message": "CWWKO1551E: 1 つ以上の依存関係が満たされていないか、リソースを有効にするフィーチャーが使用可能ではないか、このタイプのリソースの検証が不可能です。"
    }
  },
  {
    "uid": "jms_cf_qmsample",
    "id": "jms_cf_qmsample",
    "jndiName": "jms/CF_QM_SAMPLE",
    "successful": true,
    "info": {
      "jmsProviderName": "IBM MQ JMS Provider",
      "jmsProviderVersion": "8.0.0.0",
      "jmsProviderSpecVersion": "2.0"
    }
  },
  {
    "uid": "jms_cf_qmtest",
    "id": "jms_cf_qmtest",
    "jndiName": "jms/CF_QM_TEST",
    "successful": true,
    "info": {
      "jmsProviderName": "IBM MQ JMS Provider",
      "jmsProviderVersion": "8.0.0.0",
      "jmsProviderSpecVersion": "2.0"
    }
  }
]
```

## 補足：Liberty の構成情報を取得する
REAT API 形式のテスト接続機能と同じタイミングで追加された機能に、Liberty の構成情報を JSON 形式で取得する機能(REST API)があります。server.xml で指定していないパラメーターの値も含め、全ての設定が出力されるようです。

この機能を利用する場合は、以下の URL にアクセスします。尚、こちらの機能は、`reader` ロールの権限でもアクセスできます。

- **https://(ホスト名):(ポート番号)/ibm/api/config/(elementName)/(uid)**

`(elementName)` は、`dataSource` や `jmsConnectionFactory` などの最上位タグの名前のことで、`(uid)` はタグの id 属性に指定されている値です。
`(uid)` を省略すると、`(elementName)` の全ての定義が取得できます。
つまり、`https://…//ibm/api/config/dataSource` にアクセスすると、全てのデータソースの定義が取得できます。
更に、`(elementName)` も省略すると、Liberty の全ての構成情報が取得できます。

以下は、あるデータソースの構成情報を取得した例です。


```sh:実行例：データソース の構成情報の取得
$ curl --insecure -u admin:pswd https://localhost:9443/ibm/api/config/dataSource/SampleDS
{
   "configElementName": "dataSource",
   "uid": "SampleDS",
   "id": "SampleDS",
   "jndiName": "jdbc/sample",
   "beginTranForResultSetScrollingAPIs": true,
   "beginTranForVendorAPIs": true,
   "connectionSharing": "MatchOriginalRequest",
   "containerAuthDataRef": {
      "configElementName": "authData",
      "uid": "SampleAD",
      "id": "SampleAD",
      "password": "******",
      "user": "db2inst1"
   },
   "enableConnectionCasting": false,
   "jdbcDriverRef": {
      "configElementName": "jdbcDriver",
      "uid": "SampleJD",
      "id": "SampleJD",
      "libraryRef": {
         "configElementName": "library",
         "uid": "jdbcDriver[SampleJD]/library[default-0]",
         "apiTypeVisibility": "spec,ibm-api,api,stable",
         "filesetRef": [
            {
               "configElementName": "fileset",
               "uid": "jdbcDriver[SampleJD]/library[default-0]/fileset[default-0]",
               "caseSensitive": true,
               "dir": "/IBM/DB2Driver",
               "excludes": "",
               "includes": "db2jcc4.jar",
               "scanInterval": 0
            }
         ]
      }
   },
   "statementCacheSize": 10,
   "syncQueryTimeoutWithTransactionTimeout": false,
   "transactional": true,
   "type": "javax.sql.XADataSource",
   "validationTimeout": 5,
   "properties.db2.jcc": {
      "databaseName": "sample",
      "deferPrepares": true,
      "driverType": 4,
      "portNumber": 50000,
      "retrieveMessagesFromServerOnGetMessage": true,
      "serverName": "????",
      "traceLevel": 0
   },
   "api": [
      "/ibm/api/validation/dataSource/SampleDS"
   ]
}
```

## 紹介した機能(REST API)の使い方を調べる
`mpOpenApi-1.0` 以上のフィーチャーを有効にし、以下の URL にアクセスすると、今回紹介した機能(REST API)の使用方法を確認できます。

- **https://(ホスト名):(ポート番号)/openapi/platform/validation**
- **https://(ホスト名):(ポート番号)/openapi/platform/config**

今回使用していないパラメーターに関しても説明がありますので、必要に応じて参照してください。

## 終わりに
今回は、Liberty の REST API 形式のテスト接続機能を取り上げました。
頻繁に使用する機能ではないと思いますが、知っていると役に立つことがあるかと思います。