2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IBM MQ入門:コンテナで動かすハンズオン 3. 中級編 Libertyアプリによるメッセージの送受信

Last updated at Posted at 2025-08-29

1. この記事について

この記事は、IBM MQに関するハンズオン資料シリーズの一部です。
まだ「はじめに」の記事をご覧になっていない方は、まずそちらからご確認ください。

この章の位置付け

この章では、IBM webSphere Libertyを使用してコンテナでwebアプリを立ち上げ、アプリからMQへメッセージの送受信を行います。

  • このハンズオンによってできるようになること
    • IBM webSphere Libertyでアプリを実行する
    • JMSでIBM MQとwebアプリを接続する
    • アプリ上からメッセージの送受信を行う
  • このハンズオンの目的
    • IBM MQとwebアプリを接続し、メッセージを送受信するイメージを掴む
  • 対象者の前提条件
    • IBM MQの基礎を知っていること
    • IBM webSphere Libertyの基礎を知っていること
    • 初級編を実施し、QM1とQM2の接続を完了していること

前提となる環境

本資料では、以下の内容を前提の環境としています。

本資料の環境 バージョン
podman version 5.3.1 (homebrew)
java(JDK)(IBM Semeru)Java17 openjdk 17.0.13
git version 2.46.0
maven Apache Maven 3.9.9
Visual Studio Code 1.100.0
make (Apple Silicon (Arm64)のmacユーザーの場合) version 4.4.1 (homebrew)

以下の環境についてはすでに初級編で構築済みです。
まだ初級編を実施していない方は、先にそちらの手順を完了させてください。

2. 初級編 MQの基本操作(GUI版)
2. 初級編 MQの基本操作(CLI版)

  • カスタムネットワークの作成
  • QM1の作成
  • QM2の作成
  • QM1にリモートキュー作成
  • QM1にトランスミッションキュー作成
  • QM1とQM2でチャネルの作成

2. ハンズオンの全体像

image.png

3. リソースアダプターのインストール

IBM WebSphere LibertyとIBM MQの接続にはリソースアダプターが必要です。

まずはリソースアダプターをインストールします。

リソースアダプターの取得

公式ガイドを元に、リソースアダプターの取得を行います。

まずは以下のリンクにアクセスします。

https://www.ibm.com/links?url=https%3A%2F%2Fibm.biz%2Fmq94JRA

表示された選択可能なフィックスのリストから、使用しているバージョンのIBM MQ用のリソース・アダプターを見つけます。

今回は9.4.2.1-IBM-MQ-Java-InstallRAを選択し、次へ進むをクリックします。
image.png

ダウンロード・オプションを選択します。

今回は「ブラウザー (HTTPS) を使用したダウンロード」を選択し、次へ進むをクリックします。
image.png

条件を確認し、同意します。
image.png

x.x.x.x-IBM-MQ-Java-InstallRA.jarというファイル名をクリックし、ファイルをダウンロードします。
image.png

インストールの実行

コマンドプロンプトまたはターミナルを開き、以下のコマンドを実行します。

ファイル名は、自分がダウンロードした名前に合わせて適宜変更してください。

$ cd <path-to-your-installer-directory>
$ java -jar 9.4.2.1-IBM-MQ-Java-InstallRA.jar

インストーラーの指示に従い、インストールを完了してください。

インストールが完了すると、wmqというファイルが生成されます。
このファイルは後ほど使用します。

4. Liberty環境での実装手順

image.png

今回使用するソースコードリポジトリは以下の通りです。
こちらのリポジトリをgit cloneすることで、本ページ手順4の「ビルドとデプロイ」から実行することも可能です。
https://github.com/ktgrryt/mqapp-jms

ただし、初めてLibertyを触る方や、自分で手を動かしてアプリケーションの中身を確認したい方は以下の手順を順番に実行することを推奨します。

開発環境の準備

OpenLibertyを以下のURLからダウンロードします。

https://openliberty.io/start/

以下の項目を記入し、Generate Projectをクリックします。

  • Artifact: mqapp
  • Java SE Version: 17
  • Java EE/Jakarta EE Version: 9.1
  • MicroProfile Version: None

image.png

mqappというzipファイルがダウンロードされるので、zipファイルを解凍してお好みのディレクトリに保管します。

ここまでの準備が完了したら、コードエディタでmqappのフォルダを開きます。
image.png

ここから、このプロジェクトにコードを追加していきます。

server.xmlの編集

mq-app/src/main/liberty/config/にあるserver.xmlの9行目以降に以下の項目を追加します。

    <featureManager>
        <feature>jakartaee-9.1</feature>
    </featureManager>

    <resourceAdapter id="wmqjmsra" location="/config/wmq.jakarta.jmsra.rar"/>

    <jmsConnectionFactory jndiName="jms/wmqCF" connectionManagerRef="ConMgr">
        <properties.wmqjmsra
            channel="DEV.APP.SVRCONN"
            hostName="QM1"
            port="1414"
            userName="app"
            password="passw0rd"
            queueManager="QM1"
            transportType="CLIENT"
        />
    </jmsConnectionFactory>

    <connectionManager id="ConMgr" maxPoolSize="10"/>

    <jmsQueue id="jms/queue1" jndiName="jms/queue1">
        <properties.wmqjmsra baseQueueName="DEV.QUEUE.1" />
    </jmsQueue>

    <jmsQueue id="jms/remote1" jndiName="jms/remote1">
        <properties.wmqjmsra baseQueueName="REMOTE.Q.1" />
    </jmsQueue>

加えて、httpsのポート番号を以下のように9445に変更します。

    <httpEndpoint id="defaultHttpEndpoint"
                  httpPort="9080"
                  httpsPort="9445" />

また、ssl idは以下のようにコメントアウトします。

    <!-- <ssl id="defaultSSLConfig" trustDefaultCerts="true" /> -->

フロントエンドの実装

次に、フロントエンドの実装を行います。

今回はhtml, javascript, cssを使用します。

まずは、mqapp/src/mainwebappというフォルダを作成します。

webappフォルダの中に、index.htmlというファイルを作成します。
image.png

image.png

index.htmlファイルに以下のコードを追加します。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Send and Receive Message</title>
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
    <h1>MQによるメッセージの送受信</h1>

    <div class="forms-container">
        <!-- メッセージ送信フォーム -->
        <div id="send-container">
            <form id="sendForm">
                <h2>メッセージ送信</h2>
                <label for="queueSelectSend">送信先キューを選択:</label>
                <select id="queueSelectSend" required>
                    <option value="/mqapp/api/sendlocal">ローカルキュー(DEV.QUEUE.1)</option>
                    <option value="/mqapp/api/sendremote">リモートキュー(REMOTE.Q.1)</option>
                </select>
                <label for="messageInput">メッセージ:</label>
                <input type="text" id="messageInput" name="msg" required>
                <button type="button" onclick="sendMessage()">送信</button>
            </form>
        </div>
    
        <!-- メッセージ受信フォーム -->
        <div id="receive-container">
            <form id="recvForm">
                <h2>メッセージ受信</h2>
                <label for="queueSelectRecv">受信するキューを選択:</label>
                <select id="queueSelectRecv" required>
                    <option value="/mqapp/api/recv">ローカルキュー(DEV.QUEUE.1)</option>
                </select>
                <button type="button" onclick="receiveMessage()">受信</button>
            </form>
        </div>
    </div>
    
    <!-- メッセージ履歴 -->
    <div id="history-container">
        <div id="messages">
            <div id="messages-header">
                <h2>メッセージ送受信の履歴:</h2>
            </div>
            <div id="messages-list">
                <!-- メッセージがここに追加される -->
            </div>
        </div>
    </div>

    <script src="js/send.js"></script>
    <script src="js/receive.js"></script>
    <script src="js/history.js"></script>
</body>
</html>

次に、webappフォルダ内にjs, cssというフォルダを作成します。
image.png

jsフォルダの中に、以下の3つのファイルを作成します。

send.js

function sendMessage() {
    const queueEndpoint = document.getElementById('queueSelectSend').value;
    const message = document.getElementById('messageInput').value;

    if (!message) {
        alert("メッセージを入力してください");
        return;
    }

    // リクエスト実行メッセージを履歴に追加 (グレー表示)
    addMessageToHistory("メッセージの送信処理を開始しました", 'request');

    fetch(queueEndpoint, {
        method: 'POST',
        headers: {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
        },
        body: new URLSearchParams({ msg: message })
    })
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTPエラー: ${response.status}`);
        }
        return response.json();
    })
    .then(parsedData => {
        addMessageToHistory(`送信: ${parsedData.message}`, 'response', parsedData.headers);
        messageInput.value = ''; // フォームをクリア
    })
    .catch(error => {
        addMessageToHistory("送信エラー: " + error.message, 'error');
    });

}    

receive.js

function receiveMessage() {
    const queueEndpoint = document.getElementById('queueSelectRecv').value;

    // リクエスト実行メッセージを履歴に追加 (グレー表示)
    addMessageToHistory("メッセージの受信処理を開始しました", 'request');

    fetch(queueEndpoint)
    .then(response => {
        if (!response.ok) {
            throw new Error(`HTTPエラー: ${response.status}`);
        }
        return response.json();
    })
    .then(parsedData => {
        addMessageToHistory(`受信: ${parsedData.message}`, 'response', parsedData.headers);
    })
    .catch(error => {
        addMessageToHistory("受信エラー: " + error.message, 'error');
    });
    
}

history.js

function addMessageToHistory(message, type = 'default', headers = null) {
    const messagesDiv = document.getElementById('messages-list');
    const newMessageDiv = document.createElement('div');
    newMessageDiv.classList.add('message');

    // メッセージの種類に応じてクラスを追加
    if (type === 'request') {
        newMessageDiv.classList.add('message-request');
    } else if (type === 'response') {
        newMessageDiv.classList.add('message-response');
    } else if (type === 'error') {
        newMessageDiv.classList.add('message-error');
    }

    // メッセージ内容
    const messageText = document.createElement('span');
    messageText.textContent = message;
    newMessageDiv.appendChild(messageText);

    // タイムスタンプを作成
    const timestamp = document.createElement('span');
    timestamp.classList.add('timestamp');
    const now = new Date();

    // ミリ秒まで表示するためのフォーマット作成
    const hours = now.getHours().toString().padStart(2, '0');
    const minutes = now.getMinutes().toString().padStart(2, '0');
    const seconds = now.getSeconds().toString().padStart(2, '0');
    const milliseconds = now.getMilliseconds().toString().padStart(3, '0');

    // フォーマット例: HH:mm:ss.SSS
    timestamp.textContent = `${hours}:${minutes}:${seconds}.${milliseconds}`;
    newMessageDiv.appendChild(timestamp); // タイムスタンプをメッセージに追加

    // ヘッダーがある場合の処理
    if (headers) {
        const headerDiv = document.createElement('div');
        headerDiv.classList.add('message-headers');
        headerDiv.style.display = 'none'; // 初期は非表示

        Object.entries(headers).forEach(([key, value]) => {
            const headerItem = document.createElement('p');
            headerItem.innerHTML = `<strong>${key}:</strong> ${value}`;
            headerDiv.appendChild(headerItem);
        });

        newMessageDiv.appendChild(headerDiv);

        newMessageDiv.addEventListener('click', () => {
            headerDiv.style.display = headerDiv.style.display === 'none' ? 'block' : 'none';
        });
    }

    messagesDiv.insertBefore(newMessageDiv, messagesDiv.firstChild); // 新しいメッセージを上に追加
}

styles.css

cssフォルダの中に、次のファイルを追加します。

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 
    background-color: #f4f4f4; 
    color: #333;
    margin: 0;
    padding: 0;
    line-height: 1.6;
}

h1 {
    color: #333;
    text-align: center;
    margin-top: 20px;
    font-size: 2rem; 
}

.forms-container {
    display: flex;
    flex-wrap: wrap; /* レスポンシブ対応:小さい画面で折り返す */
    justify-content: center; /* 中央揃え */
    gap: 20px; /* フォーム間のスペースを広げる */
    margin: 20px auto;
    padding: 20px;
    max-width: 1200px; /* 全体の最大幅を設定 */
    box-sizing: border-box; /* パディングを含めて幅を計算 */
}

form {
    padding: 20px;
    background-color: #ffffff;
    border: 1px solid #e0e0e0;
    border-radius: 10px; /* 角をより丸く */
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    flex: 1 1 300px; /* レスポンシブ対応:最低幅300px */
    width: 400px; /* フォームの最大幅を指定 */
    box-sizing: border-box;
}

label {
    display: block;
    margin-bottom: 8px;
    font-weight: 600; 
    color: #555;
}

input[type="text"], select {
    width: 100%; 
    padding: 12px;
    margin-bottom: 20px;
    border: 1px solid #ccc;
    border-radius: 6px;
    box-sizing: border-box; 
    font-size: 1rem; 
    transition: border-color 0.3s, box-shadow 0.3s; /* フォーカス時のアニメーション */
}

input[type="text"]:focus, select:focus {
    border-color: #007BFF; /* フォーカス時のボーダー色 */
    box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); /* フォーカス時の影 */
    outline: none; /* デフォルトのアウトラインを削除 */
}

button {
    background-color: #007BFF;
    color: #ffffff;
    border: none;
    padding: 12px 24px;
    border-radius: 6px;
    cursor: pointer;
    font-size: 1rem;
    font-weight: 600; /* ボタンの文字を強調 */
    transition: background-color 0.3s, transform 0.2s;
}

button:hover {
    background-color: #0056b3;
    transform: translateY(-2px); /* ホバー時に少し浮き上がるアニメーション */
}

button:active {
    background-color: #003d80; /* クリック時の色 */
    transform: translateY(0); /* クリック時は元の位置に戻る */
}

#messages {
    margin: 20px auto;
    padding: 20px;
    background-color: #ffffff;
    border: 1px solid #e0e0e0;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    max-width: 820px; /* メッセージコンテナの最大幅を指定 */
    box-sizing: border-box;
}

.message {
    margin-bottom: 10px;
    padding: 15px; /* パディングを広げる */
    border: 1px solid #ddd;
    border-radius: 6px; /* 角を少し丸く */
    background-color: #f9f9f9;
    font-size: 0.95rem; /* フォントサイズを調整 */
    color: #555;
    line-height: 1.4; /* 行間を広げる */
    position: relative; /* タイムスタンプを絶対位置で配置するために必要 */
}

.timestamp {
    position: absolute;
    top: 5px;
    right: 10px;
    font-size: 12px;
    color: #999;
}

.message-request {
    color: #888; /* グレー表示 */
    background-color: #f5f5f5; /* 薄い背景色 */
    border-left: 4px solid #ccc; /* 左にアクセントライン */
}

.message-response {
    color: #007BFF; /* 青色で強調 */
    background-color: #eef6ff; /* 薄い青の背景色 */
    border-left: 4px solid #007BFF; /* 左にアクセントライン */
}

.message-error {
    color: #ff0000; /* 赤色の文字 */
    background-color: #ffe6e6; /* 薄い赤色の背景 */
    border-left: 4px solid #ff0000; /* 左に赤色のアクセントライン */
}

/* メッセージ履歴ヘッダーのスタイル */
#messages-header {
    position: relative;
    padding: 10px;
    border-bottom: 1px solid #ddd;
}

.message-headers {
    margin-top: 10px;
    padding: 10px;
    background-color: #f1f1f1;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 0.9rem;
    color: #555;
    line-height: 1.4;
}

/* レスポンシブ対応 */
@media (max-width: 860px) {
    #messages {
        max-width: 400px; /* 横幅の最大値を400pxに制限 */
        width: 100%; /* 幅を100%に設定 */
    }
}

以上でフロントエンドの実装は完了です。

webappフォルダ内が以下の構成になっていることを確認してください。

image.png

また、作成したファイルが保存されていることを確認してください。

バックエンドの実装

次に、バックエンドの実装を行います。

mqapp/src/main/java/com/demo/restフォルダにMessageApi.javaというファイルを追加します。

MessageApi.java

package com.demo.rest;

import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;

import com.demo.messaging.MQConsumer;
import com.demo.messaging.MQProducer;

@Stateless
@Path("/")
public class MessageApi {

    @Inject
    MQProducer producer;

    @Inject
    MQConsumer consumer;
    
    @POST
    @Path("/sendlocal")
    public Response sendLocal(@FormParam("msg") String message) {
        try {
            String result = producer.sendLocalMessage(message);
            return Response.ok(result).build(); // HTTP 200 OK を返す
        } catch (Exception e) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                           .entity("ローカルキューへの送信に失敗しました: " + e.getMessage())
                           .build(); // HTTP 500 Internal Server Error を返す
        }
    }
    
    @POST
    @Path("/sendremote")
    public Response sendRemote(@FormParam("msg") String message) {
        try {
            String result = producer.sendRemoteMessage(message);
            return Response.ok(result).build(); // HTTP 200 OK を返す
        } catch (Exception e) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                           .entity("リモートキューへの送信に失敗しました: " + e.getMessage())
                           .build(); // HTTP 500 Internal Server Error を返す
        }
    }
    
    @GET
    @Path("/recv")
    public Response recv() {
        try {
            String result = consumer.recvMessage();
            return Response.ok(result).build(); // HTTP 200 OK を返す
        } catch (Exception e) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
                           .entity("Error: " + e.getMessage())
                           .build(); // HTTP 500 Internal Server Error を返す
        }
    }
    
}

次に、mqapp/src/main/java/com/demomessagingというフォルダを追加し、以下の2つのファイルを追加します。

MQConsumer.java

package com.demo.messaging;

import jakarta.annotation.Resource;
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.jms.JMSConnectionFactory;
import jakarta.jms.JMSContext;
import jakarta.jms.JMSConsumer;
import jakarta.jms.Destination;
import jakarta.jms.Message;
import jakarta.jms.TextMessage;

import jakarta.json.Json;
import jakarta.json.JsonObject;

@Stateless
public class MQConsumer {

    @Inject
    @JMSConnectionFactory("jms/wmqCF")
    JMSContext context;

    @Resource(lookup = "jms/queue1")
    Destination dest;

    // タイムアウト時間(ミリ秒単位)
    private static final long RECEIVE_TIMEOUT = 5000; // 例: 5秒


    public String recvMessage() throws Exception {
        try {
            JMSConsumer consumer = context.createConsumer(dest);

            // タイムアウト付きで受信
            Message message = consumer.receive(RECEIVE_TIMEOUT);

            if (message == null) {
                throw new Exception("メッセージ受信タイムアウト");
            }

            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;

                // JMS ヘッダー情報を取得
                JsonObject headers = Json.createObjectBuilder()
                        .add("JMSMessageID", textMessage.getJMSMessageID())
                        .add("JMSTimestamp", textMessage.getJMSTimestamp())
                        .add("JMSCorrelationID", textMessage.getJMSCorrelationID() != null ? textMessage.getJMSCorrelationID() : "")
                        .add("JMSDestination", textMessage.getJMSDestination() != null ? textMessage.getJMSDestination().toString() : "")
                        .add("JMSDeliveryMode", textMessage.getJMSDeliveryMode())
                        .add("JMSExpiration", textMessage.getJMSExpiration())
                        .add("JMSPriority", textMessage.getJMSPriority())
                        .add("JMSReplyTo", textMessage.getJMSReplyTo() != null ? textMessage.getJMSReplyTo().toString() : "")
                        .add("JMSType", textMessage.getJMSType() != null ? textMessage.getJMSType() : "")
                        .build();

                // メッセージとヘッダー情報をJSON形式で返す
                return Json.createObjectBuilder()
                        .add("message", textMessage.getText())
                        .add("headers", headers)
                        .build()
                        .toString();
            } else {
                throw new Exception("受信したメッセージがテキスト形式ではありません");
            }
        } catch (Exception e) {
            throw new Exception("メッセージの受信に失敗しました  " + e.getMessage(), e);
        }
    }

}

MQProducer.java

package com.demo.messaging;

import jakarta.annotation.Resource;
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.jms.JMSConnectionFactory;
import jakarta.jms.JMSContext;
import jakarta.jms.Queue;
import jakarta.jms.TextMessage;

import jakarta.json.Json;
import jakarta.json.JsonObject;

@Stateless
public class MQProducer {

    @Inject
    @JMSConnectionFactory("jms/wmqCF")
    JMSContext context;

    @Resource(lookup = "jms/queue1")
    Queue queue;

    @Resource(lookup = "jms/remote1")
    Queue remoteQueue;

    // ローカルキューにメッセージを送信するメソッド
    public String sendLocalMessage(String message) throws Exception {
        try {
            TextMessage textMessage = context.createTextMessage();
            textMessage.setText(message);
            context.createProducer().send(queue, textMessage);

            // JMS ヘッダー情報を取得
            JsonObject headers = Json.createObjectBuilder()
                    .add("JMSMessageID", textMessage.getJMSMessageID())
                    .add("JMSTimestamp", textMessage.getJMSTimestamp())
                    .add("JMSCorrelationID", textMessage.getJMSCorrelationID() != null ? textMessage.getJMSCorrelationID() : "")
                    .add("JMSDestination", textMessage.getJMSDestination() != null ? textMessage.getJMSDestination().toString() : "")
                    .add("JMSDeliveryMode", textMessage.getJMSDeliveryMode())
                    .add("JMSExpiration", textMessage.getJMSExpiration())
                    .add("JMSPriority", textMessage.getJMSPriority())
                    .add("JMSReplyTo", textMessage.getJMSReplyTo() != null ? textMessage.getJMSReplyTo().toString() : "")
                    .add("JMSType", textMessage.getJMSType() != null ? textMessage.getJMSType() : "")
                    .build();

            // メッセージとヘッダー情報をJSON形式で返す
            return Json.createObjectBuilder()
                    .add("message", message)
                    .add("headers", headers)
                    .build()
                    .toString();
        } catch (Exception e) {
            throw new Exception("ローカルキューへの送信に失敗しました  " + e.getMessage(), e);
        }
    }

    // リモートキューにメッセージを送信するメソッド
    public String sendRemoteMessage(String message) throws Exception {
        try {
            TextMessage textMessage = context.createTextMessage();
            textMessage.setText(message);
            context.createProducer().send(remoteQueue, textMessage);

            // JMS ヘッダー情報を取得
            JsonObject headers = Json.createObjectBuilder()
                    .add("JMSMessageID", textMessage.getJMSMessageID())
                    .add("JMSTimestamp", textMessage.getJMSTimestamp())
                    .add("JMSCorrelationID", textMessage.getJMSCorrelationID() != null ? textMessage.getJMSCorrelationID() : "")
                    .add("JMSDestination", textMessage.getJMSDestination() != null ? textMessage.getJMSDestination().toString() : "")
                    .add("JMSDeliveryMode", textMessage.getJMSDeliveryMode())
                    .add("JMSExpiration", textMessage.getJMSExpiration())
                    .add("JMSPriority", textMessage.getJMSPriority())
                    .add("JMSReplyTo", textMessage.getJMSReplyTo() != null ? textMessage.getJMSReplyTo().toString() : "")
                    .add("JMSType", textMessage.getJMSType() != null ? textMessage.getJMSType() : "")
                    .build();

            // メッセージとヘッダー情報をJSON形式で返す
            return Json.createObjectBuilder()
                    .add("message", message)
                    .add("headers", headers)
                    .build()
                    .toString();
        } catch (Exception e) {
            throw new Exception("リモートキューへの送信に失敗しました  " + e.getMessage(), e);
        }
    }
}

以上でバックエンドの実装は完了です。

フォルダ内が以下のようになっていることを確認してください。また、作成したファイルが保存されていることを確認してください。
image.png

リソースアダプターの配置

mqappフォルダの直下にibmというフォルダを作成します。

ibmフォルダ内に、本章のセクション3. リソースアダプターのインストールでインストールしたwmqというフォルダ内のwmq.jakarta.jmsra.rarというファイルを配置してください。

Dockerfileの編集

Dockerfileを以下の内容に変更してください。

FROM icr.io/appcafe/open-liberty:kernel-slim-java17-openj9-ubi

COPY --chown=1001:0 /src/main/liberty/config /config

COPY --chown=1001:0 ibm/wmq.jakarta.jmsra.rar /config

RUN features.sh

COPY --chown=1001:0 target/*.war /config/apps

RUN configure.sh

以上でLiberty環境の構築は完了です。

ビルドとデプロイ

それでは作成したアプリケーションをビルドし、デプロイしましょう。

ターミナルを開き、Libertyのプロジェクトがあるディレクトリに移動します。

cd <path-to-liberty-project>

以下のコマンドを実行してプロジェクトをビルドします。BUILD SUCCESSという表記が出ることを確認してください。

mvn package

コンテナイメージを作成します。

podman build . --platform linux/x86_64 -t mqapp:1

イメージが正常に作成されたことを確認します。

podman images

以下のように、mqappというイメージが作成されていることを確認してください。

REPOSITORY                                   TAG                            IMAGE ID      CREATED        SIZE
localhost/mqapp                              1                              37d2f05da9dc  4 minutes ago  903 MB

コンテナを起動します。

podman run --name mqapp -d -p 9080:9080 -p 9445:9445 --network mq-network mqapp:1

アプリケーションにアクセスします。以下のURLにアクセスしてください。

http://localhost:9080/mqapp/

5. Libertyアプリケーションの操作

ローカルキューへの送信

image.png

URLにアクセスすると、以下の画面が表示されます。
image.png

このアプリケーションでは、ブラウザ上からキューマネージャーに対してメッセージの送受信を行うことが可能です。

送信先キューがDEV.QUEUE.1になっていることを確認し、メッセージを入力します。今回は試しにlibertyと入力し、送信を押します。
image.png

送信に成功すると、画面下部の「メッセージ送受信の履歴」に送信したメッセージの内容が表示されているのが確認できます。
image.png

履歴の表示の右上には、処理が実行された時刻が表示されます。

ブラウザ上で送信処理が実行されてから、実際にメッセージが送信されるまでの間にわずかに時間があるのが確認できます。
image.png

同様に、メッセージの内容を変えていくつかメッセージをローカルキューに送信してみましょう。
image.png

ローカルキューからの受信

image.png

次に、ローカルキューに保存されているメッセージを受信してみましょう。

受信するキューがローカルキューになっているのを確認し、受信ボタンをクリックします。
image.png

受信に成功すると、ローカルキューに入っているメッセージを取り出すことができているのを確認することができます。
image.png

ローカルキューに入っているメッセージを全て受信してみましょう。

これ以上取り出すことのできるメッセージがない状態で受信ボタンを押すと、エラーが表示されます。
image.png

このタイムアウトエラーは、mqapp内のMQConsumer.javaで5秒に設定してあります。

送信と受信を何度か行い、メッセージがFirst In First Outの順番で取り出されることを確認してください。

リモートキューへの送信

image.png

次に、リモートキューへメッセージを送信し、QM1からQM2へメッセージを飛ばしてみましょう。

送信先キューをリモートキュー(REMOTE.Q.1)に設定し、適当なメッセージを入力してから送信ボタンを押してください。
image.png

しかし、結果はエラーになってしまいます。

image.png

原因は、リモートキューに対して権限の設定を実施していないためです。

それでは、QM1にアクセスして権限の設定を行います。

以下のURLにアクセスしてください。

https://localhost:9443/ibmmq/console

ユーザー名にadmin、パスワードにpassw0rdと入力してログインします。
image.png

QM1の管理をクリックします。

image.png

キュータブをクリックし、REMOTE.Q.1の3点リーダーから構成の表示をクリックします。
image.png

セキュリティータブをクリックし、追加をクリックします。
image.png

ユーザー名にappと入力し、「管理アクセス権限」から書き込みにチェックをして、追加をクリックします。
image.png

権限が無事に追加されたことを確認します。
image.png

それでは、再度リモートキューにメッセージを送信してみましょう。

Libertyのアプリの画面に戻り、送信先キューをリモートキュー(REMOTE.Q.1)に設定し、適当なメッセージを入力してから送信ボタンを押してください。
image.png

無事にメッセージが送信されたことを確認してください。
image.png

送信されたメッセージは、初級編で作成したチャネルを通ってQM2へと伝送されます。

以下のURLからQM2のコンソールにアクセスしてください。

https://localhost:9444/ibmmq/console

ユーザー名にadmin、パスワードにpassw0rdと入力してログインします。
image.png

QM2の管理をクリックします。
image.png

キュータブから、DEV.QUEUE.2をクリックします。
image.png

メッセージが無事に送信されていることを確認してください。
image.png

もしメッセージがQM2に送信されていない場合、MQネットワークタブのキュー・マネージャー・チャネルから、QM1toQM2が実行中になっているか確認してください。

6.コンテナの停止

次に、QM2のコンテナを停止した状態でREMOTE.Q.1にメッセージを送信してみましょう。

まずはQM2を停止します。ターミナルで以下のコマンドを入力してください。

podman stop QM2

mqappを開き、送信先キューをリモートキューにして適当なメッセージを入力し、送信ボタンを押します。
image.png

メッセージが送信されたことを確認してください。
image.png

QM2のコンテナは停止しているので、QM2のwebコンソールにはアクセスできません。
image.png

QM1のwebコンソールにアクセスし、キュータブからTRANS.Q.1をクリックします。
image.png

QM2への伝送に失敗したメッセージが、TRANS.Q.1に保管されていることを確認します。
image.png

停止したコンテナを再起動します。

podman start QM2

7.JMSのヘッダー情報を確認する

JMSには、MQIと同様に、メッセージヘッダーに様々な情報が格納されています。このセクションでは、送信したメッセージのヘッダー情報を確認します。

mqappを開き、ローカルキューに対して適当な内容のメッセージを送信してください。
image.png

メッセージ送受信の履歴の中から送信したメッセージをクリックすると、JMSのヘッダー情報を確認する事ができます。
image.png

受信ボタンをクリックして、先ほど送信したメッセージが受信されるまで受信を行います。
image.png

受信したメッセージについても同様に、ヘッダー情報を確認する事ができます。
image.png

送信したメッセージと受信したメッセージを比較して、対応するメッセージはJMSMessageIDが同じであることを確認してください。
image.png

8. 環境の削除

ハンズオンをやり直す場合やここで終了する場合など、今回作成した環境を削除したい場合は、以下の手順を実行してください。

コンテナIDの確認

podman ps

コンテナの停止

podman stop <container id>

コンテナの消去

podman rm <container id>

イメージの確認

podman images

イメージの消去

podman rmi <image id>

マウントされているボリュームの確認

podman volume ls

マウントされているボリュームの消去

podman volume rm <volume name>

カスタムネットワークの確認

podman network rm

カスタムネットワークの消去

podman network rm mq-network

加えて、Libertyのアプリケーション(mqapp)も別途削除してください。

9. ハンズオン資料リンク

1. はじめに

2. 初級編 MQの基本操作(GUI版)

2. 初級編 MQの基本操作(CLI版)

3. 中級編 Libertyアプリによるメッセージの送受信(本記事)

さらに詳しい情報をお探しの方へ

IBMの最新情報、イベント情報、さらに役立つ資料は、以下のIBM Communityでも発信・格納されています。 

最新のトレンドや有益な情報をチェックするために、ぜひご覧ください!

  WebSphere関連の最新情報やディスカッション、イベント情報、技術資料を公開中!

 

  ELMに関する最新のイベント情報、ナレッジ共有、便利なドキュメントをチェック!

 

  Integrationに関する最新のイベント情報、ナレッジ共有、便利なドキュメントをチェック!

2
1
0

Register as a new user and use Qiita more conveniently

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?