付録F.3 カスタムアダプタ開発フレームワークのサンプルプログラム

ここでは,カスタムアダプタ開発フレームワークを使用するカスタムアダプタのサンプルプログラムについて説明します。

<この項の構成>
(1) サンプルプログラムの概要
(2) 電文フォーマット
(3) サンプルコード
(4) プロパティファイル
(5) プロトコルコンバータJARファイル
(6) EARファイル

(1) サンプルプログラムの概要

サンプルプログラムでは,カウント増加要求(increment),またはカウント減少要求(decrement)を受け付け,カウンタサービスを呼び出すカスタムアダプタを作成します。

カウンタサービスを呼び出すカスタムアダプタの概要を次の図に示します。

図F-2 カウンタサービスを呼び出すカスタムアダプタの概要

[図データ]

次にカウンタサービス,およびカスタムアダプタのプロトコルコンバータについて説明します。

(a) カウンタサービス

カウンタサービスは,カウントを保持します。

サンプルプログラムのカウンタサービスは通常のJavaクラスですが,サービスに見立てています。そのため,カウンタサービスの格納先は,プロトコルコンバータと同じJARになります。

カウンタサービスは次に示す三つのサービスを提供します。

(b) プロトコルコンバータ

プロトコルコンバータは,カウンタサービスを呼び出します。

プロトコルコンバータは,次に示す二つのオペレーションを持ちます。

(2) 電文フォーマット

サンプルプログラムで使用する電文形式を次に示します。

(a) incrementの要求電文フォーマット

1バイトの符号付の2進整数を一つ取るバイナリフォーマット定義ファイルを作成します。ファイル名は,「BinaryRequest.fdx」にしてください。

バイナリフォーマット定義ファイルの作成方法については,「4.4 電文フォーマット(バイナリフォーマット定義ファイル)の作成方法」を参照してください。

(b) decrementの要求電文フォーマット

decrementの要求電文フォーマット(XMLフォーマット定義ファイル)は,テキストエディタなどを使用してXML文書形式で作成します。ファイル名は「XMLRequest.xsd」にしてください。

XMLフォーマット定義ファイルの作成方法については,「4.3 電文フォーマット(XMLフォーマット定義ファイル)の作成方法」を参照してください。

作成するXMLフォーマット定義ファイルの内容を次に示します。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/RequestXML" xmlns:tns="http://www.example.org/RequestXML">
   <element name="request" type="string"></element>
</schema>

(c) decrementの応答電文フォーマット

decrementの応答電文フォーマット(XMLフォーマット定義ファイル)は,テキストエディタなどを使用してXML文書形式で作成します。ファイル名は「XMLResponse.xsd」にしてください。

XMLフォーマット定義ファイルの作成方法については,「4.3 電文フォーマット(XMLフォーマット定義ファイル)の作成方法」を参照してください。

作成するXMLフォーマット定義ファイルの内容を次に示します。

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/RequestXML" xmlns:tns="http://www.example.org/RequestXML">
   <element name="response" type="string"></element>
</schema>

(3) サンプルコード

サンプルプログラムで使用するカウンタサービス,およびプロトコルコンバータのサンプルコードについて説明します。

(a) カウンタサービス

カウンタサービスのサンプルコードを次に示します。クラス名は「Counter」,パッケージ名は「service」です。

/* All Rights Reserved. Copyright (C) 2008, Hitachi, Ltd. */
package service;

/**
* カウント値の増減とカウント値の取得クラス(サービスに見立てる)
* カウント値は常に0以上
*/
public class Counter {

   /**
    * カウント値 (常に0以上)
    */
   protected int count;

   /**
    * コンストラクタ
    * @param init countの初期値
    * @throws IllegalArgumentException不正な0未満の値を指定しました。
    * @return void
    */
   public Counter(int init) {

       if (init < 0) {
             throw new IllegalArgumentException();
       }
             this.count = init;
   }

   /**
    * カウントを一つ増やす
    * @return void
    */
   public void increment() {
       ++this.count;
   }


   /**
    * カウントを一つ減らす
    * @throws IllegalStateException countが0以下の時に呼び出しました。
    * @return void
    */
   public void decrement() {
       if (this.count <= 0) {
             throw new IllegalStateException();
       }
       --this.count;
   }

   /**
    * カウント値を取得する
    * @return int カウント値
    */
   public int getCount() {
       return this.count;
   }
}

(b) プロトコルコンバータ

プロトコルコンバータのサンプルコードを次に示します。クラス名は「MyProtocolConverter」,パッケージ名は「protocolconverter」です。

/* All Rights Reserved. Copyright (C) 2008, Hitachi, Ltd. */
package protocolconverter;

import java.util.Properties;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import jp.co.Hitachi.soft.csc.msg.adapter.custom.*;

import service.Counter;

/**
* カスタムプロトコルコンバータインターフェースの実装クラス
*/
public class MyProtocolConverter implements CSCMsgCustomProtocolConverter {

   /**
    * アダプタコンテキスト
    */
   private CSCMsgCustomAdapterContext adapterContext;

   /**
    * カウンタクラス(サービスに見立てている)
    */
   private Counter counter;

   /**
    * DOM. XML電文を作成するために使用
    */
   private DocumentBuilderFactory docBuilderFactory;
   private DocumentBuilder docBuilder;

   /**
    * publicなデフォルトコンストラクタ (必須)
    */
   public MyProtocolConverter() { }

   /**
    * カスタムアダプタ開始時(startが呼ばれる前)に呼ばれます。
    * アダプタコンテキストを取得します。
    * @param adapterContext アダプタコンテキスト
    */
   public void setCustomAdapterContext(
            CSCMsgCustomAdapterContext adapterContext) {
       this.adapterContext = adapterContext;
   }

   /**
    * カスタムアダプタ開始時(setCustomAdapterContextが呼ばれた後)に呼ばれます。
    * 初期化処理を実装します。
    * @throws CSCMsgCustomAdapterException カスタムアダプタで例外が発生しました。
    */
   public void start() throws CSCMsgCustomAdapterException {
       try {
            log("start");

            Properties properties = this.adapterContext.getProperties();
            String init = properties.getProperty("init");
            if (init == null) {
                 // init値の指定は必須
                 throw new CSCMsgCustomAdapterException("init");
           }

           // リソースの確保(アダプタのライフサイクル中使い続けるリソース)
           this.counter = new Counter(Integer.parseInt(init));

           docBuilderFactory = DocumentBuilderFactory.newInstance();
           //docBuilderFactory.setNamespaceAware(true);
           docBuilder = docBuilderFactory.newDocumentBuilder();
       } catch (CSCMsgCustomAdapterException e) {
           // カスタムアダプタ側で障害解析に必要なログを出力する
           log(e);
           throw e;
       } catch (Exception e) { // RuntimeExceptionもキャッチする
           // カスタムアダプタ側で障害解析に必要なログを出力する
           log(e);
           // CSCMsgCustomAdapterExceptionでラップして再送出
           throw new CSCMsgCustomAdapterException("start failed", e);
       }
   }

   /**
    * サービス部品呼び出し処理を実装します。
    * @param request 要求メッセージ
    * @param response 応答メッセージ
    * @throws CSCMsgCustomAdapterException カスタムアダプタで例外が発生しました。
    */
   public void invoke(CSCMsgRequestMessage request,
            CSCMsgResponseMessage response)
            throws CSCMsgCustomAdapterException {

       try {
            log("invoke [" + request.getOperationName() + "]");

           // オペレーション名で振り分け
           if (request.getOperationName().equals("increment")) {
                 // カウント増加処理
                 execIncrement(request, response);
           } else if(request.getOperationName().equals("decrement")) {
                 // カウント減少処理
                 try {
                      execDecrement(request, response);
                 } catch(IllegalStateException e) { // カウントの減少に失敗
                      // ログ出力
                      log(e);
                      // フォルト設定
                      response.setFault("invoke error", "decrement failed",
                                this.adapterContext.getAdapterName(), (Document)null);
                 }
           } else {
                throw new CSCMsgCustomAdapterException("unknown operation");
           }
           // 現在のカウントを出力
           log("Count:" + counter.getCount());
       } catch (CSCMsgCustomAdapterException e) {
           // カスタムアダプタ側で障害解析に必要なログを出力する
           log(e);
           throw e;
       } catch (Exception e) { // RuntimeExceptionもキャッチする
           // カスタムアダプタ側で障害解析に必要なログを出力する
           log(e);
           // CSCMsgCustomAdapterExceptionでラップして再送出
           throw new CSCMsgCustomAdapterException("invoke failed", e);
       }
   }

   /**
    * カウント増加処理
    * @param request 要求メッセージ
    * @param response 応答メッセージ
    * @throws CSCMsgCustomAdapterException カスタムアダプタで例外が発生しました。
    * @throws CSCMsgIllegalMessageTypeException 電文形式がバイナリ形式ではありません。
    * @throws CSCMsgInvalidMessageException 不正な電文が指定されました。
    */
   private void execIncrement(
             CSCMsgRequestMessage request,
             CSCMsgResponseMessage response)
       throws CSCMsgCustomAdapterException,
             CSCMsgIllegalMessageTypeException,
             CSCMsgInvalidMessageException {
       // 非同期オペレーション:increment

       if (response != null) {
             // 通信モデルが同期
             throw new CSCMsgCustomAdapterException("不正な通信モデル");
       }

       if (request.getMessageType() != CSCMsgMessageConstant.MESSAGE_TYPE_BINARY) {
             // 電文フォーマットがバイナリ形式でない
             throw new CSCMsgCustomAdapterException("不正な電文フォーマット(要求)");
       }

       byte[] data = request.getBytes();
       if (data.length != 1) {
           throw new CSCMsgCustomAdapterException("不正なデータ");
       }

       int loop = Math.abs(data[0]);
       for (int i = 0; i < loop; ++i) {
            counter.increment();
       }
   }

   /**
    * カウント減少処理
    * @param request 要求メッセージ
    * @param response 応答メッセージ
    * @throws CSCMsgCustomAdapterException カスタムアダプタで例外が発生しました。
    * @throws CSCMsgIllegalMessageTypeException 電文形式がバイナリ形式ではありません。
    * @throws CSCMsgInvalidMessageException 不正な電文が指定されました。
    * @throws CSCMsgMultipleMessageInsertionException すでに電文またはフォルト情報が格納されています。
    */
   private void execDecrement(
            CSCMsgRequestMessage request,
            CSCMsgResponseMessage response)
       throws CSCMsgCustomAdapterException,
            CSCMsgIllegalMessageTypeException,
            CSCMsgInvalidMessageException,
            CSCMsgMultipleMessageInsertionException  {
       // 同期オペレーション:decrement

       if (response == null) {
             // 通信モデルが非同期
             throw new CSCMsgCustomAdapterException("不正な通信モデル");
       }

       if (request.getMessageType() != CSCMsgMessageConstant.MESSAGE_TYPE_XML) {
             // 電文フォーマットがXML形式でない
             throw new CSCMsgCustomAdapterException("不正な電文フォーマット(要求)");
       }

       if (response.getMessageType() != CSCMsgMessageConstant.MESSAGE_TYPE_XML) {
             // 電文フォーマットがXML形式でない
             throw new CSCMsgCustomAdapterException("不正な電文フォーマット(応答)");
       }

       // 要求電文解析処理
       NodeList nodeList = request.getXMLDocument().getElementsByTagName("request");
       if (nodeList.getLength() != 1) {
             // ルートノードが見つからない
             throw new CSCMsgCustomAdapterException("不正な要求電文");
       }
       Text text = (Text)nodeList.item(0).getChildNodes().item(0);

       int loop = Math.abs(Integer.parseInt(text.getNodeValue()));
       for (int i = 0; i < loop; ++i) {
            this.counter.decrement();
       }

       // 応答電文の組み立て
       Document responseDoc = docBuilder.newDocument();
       Element rootElement = responseDoc.createElement("response");
       responseDoc.appendChild(rootElement);
       Text countTextNode = responseDoc.createTextNode(Integer.toString(counter.getCount()));
       rootElement.appendChild(countTextNode);

       // 応答電文の格納
       response.setXMLDocument(responseDoc);
   }

   /**
    * 終了処理,リソースの解放などを行う
    */
   public void stop() {
       log("stop");

       // リソースの解放
       this.counter = null;
       this.adapterContext = null;
       this.docBuilderFactory = null;
       this.docBuilder = null;
   }

   /**
    * ログ出力
    * @param message 出力するメッセージ
    */
   public void log(String message) {
       // 標準出力への出力結果は,ユーザ出力ログに出力される
       System.out.printf("[%s] %s¥n",
               this.adapterContext.getAdapterName(), message);
               System.out.flush();
   }

   /**
    * ログ出力
    * @param message 出力する例外
    */
   public void log(Exception e) {
       // 標準エラー出力への出力結果は,ユーザエラーログに出力される
       e.printStackTrace();
       System.err.flush();
   }
}

(4) プロパティファイル

サンプルプログラムで使用するプロパティファイルについて説明します。

(a) カスタムアダプタ開発フレームワーク動作定義ファイル

カスタムアダプタ開発フレームワーク動作定義ファイルには,プロトコルコンバータのクラス名をパッケージ名付きで指定します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="classname">protocolconverter.MyProtocolConverter</entry>
</properties>

(b) カスタムアダプタプロパティファイル

カスタムアダプタプロパティファイルには,プロトコルコンバータで使用するプロパティを設定します。

カウンタサービスのカウントの初期値を1に設定するプロパティファイルを次に示します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="init">1</entry>
</properties>

(5) プロトコルコンバータJARファイル

プロトコルコンバータクラスファイルとカスタムアダプタ開発フレームワーク動作定義ファイルを含むJARファイルを作成します。

プロトコルコンバータJARファイルの作成は,jarコマンドを使用して,プロトコルコンバータクラスファイルとそれが使用するクラスファイル,およびカスタムアダプタ開発フレームワーク動作定義ファイルをJARファイルにアーカイブします。

jarコマンドによるアーカイブ時のJARファイルのディレクトリ構成については,「5.3.6(5) JARファイルの作成」を参照してください。

(a) 事前準備

カレントディレクトリおよびサブディレクトリに,作成したファイルを次に示すように配置します。

framework_properties.xml
protocolconverter/CustomProtocolConverter.class
service/Counter.class

(b) ファイルの作成

ここでは,作成するJARファイルを「MyProtocolConverter.jar」というファイル名でアーカイブします。

次のコマンドを実行すると,JARファイルが作成されます。

jar cf ..¥MyProtocolConverter.jar .¥

(6) EARファイル

カスタムアダプタの新規登録に必要なEARファイルを作成します。ここでは,作成するEARファイルを「CustomAdapter.ear」というファイル名でアーカイブします。

EARファイルの作成はjarコマンドを使用し,Cosminexus サービスプラットフォームが提供しているJARファイルをEARファイルにアーカイブします。

jarコマンドによるアーカイブ時のEARファイルのディレクトリ構成については,「5.3.6(6) EARファイルの作成」を参照してください。

(a) 事前準備

カレントディレクトリに次に示すファイルのコピーを格納します。

<Cosminexusのインストールディレクトリ>¥CSC¥lib¥cscmsg_adpejb.jar

(b) ファイルの作成

作成するEARファイルのファイル名は「CustomAdapter.ear」にしてください。

次のコマンドを実行すると,EARファイルが作成されます。

jar cf ..¥CustomAdapter.ear .¥

(c) ファイルの登録

カスタムアダプタとして使用するには,アダプタ追加ウィザードを利用し,作成したEARファイルをHCSC-Definerに登録する必要があります。登録方法については,「5.2.3 カスタムアダプタを新規に追加する」を参照してください。