ファイル受信アダプタの使用法
この章では、ファイル受信アダプタ (EnsLib.File.InboundAdapterOpens in a new tab) の使用方法について説明します。
InterSystems IRIS® では、このアダプタを使用する特殊なビジネス・サービス・クラスとユーザのニーズに適したビジネス・サービス・クラスの 1 つも提供されます。そのため、プログラミングの必要がありません。"相互運用プロダクションの概要" の “接続オプション” のセクションを参照してください。
全般的な動作
EnsLib.File.InboundAdapterOpens in a new tab は、構成された場所でファイルを検索したり、入力を読み取ったり、入力をストリームとして関連するビジネス・サービスに送信したりします。ユーザが作成および構成するビジネス・サービスは、このストリームを使用してプロダクションの他の部分と通信します。受信ファイル・アダプタが構成された場所に複数のファイルを見つけた場合は、ファイルの最終変更時刻に基づいて、それが早いものから順番に処理します。ただし、アダプタは、時刻値の小数点以下の秒数は無視します。そのため、2 つ以上のファイルの変更日付と時刻が、時刻の小数点以下の秒数部分のみ異なる場合は、アダプタはこれらを任意の順序で処理します。
以下の図は、全体的なフローを示しています。
詳細は、以下のとおりです。
-
アダプタは、構成されたデータ・ソースからの入力を検出するたびに、ビジネス・サービス・クラスの内部 ProcessInput() メソッドを呼び出して、ストリームを入力引数として渡します。
-
ビジネス・サービス・クラスの内部 ProcessInput() メソッドが実行されます。このメソッドは、すべてのビジネス・サービスが必要とする内部情報の保持など、基本的なプロダクション・タスクを実行します。ビジネス・サービス・クラスが継承するこのメソッドは、カスタマイズや上書きを行いません。
-
次に、ProcessInput() メソッドがカスタムの OnProcessInput() メソッドを呼び出し、ストリーム・オブジェクトを入力として渡します。このメソッドに関する要件については、後述の “OnProcessInput() メソッドの実装” で説明します。
応答メッセージは、同じパスを逆向きにたどります。
受信アダプタを使用するビジネス・サービスの作成
このアダプタをプロダクションで使用するには、ここに記載されているように新しいビジネス・サービスを作成します。後で、そのサービスをプロダクションに追加して構成します。存在しなければ、該当するメッセージ・クラスを作成する必要もあります。"プロダクションの開発" の “メッセージの定義” を参照してください。
ビジネス・サービス・クラスの基本要件を以下に列挙します。
-
ビジネス・サービス・クラスは Ens.BusinessServiceOpens in a new tab を拡張するものでなければなりません。
-
クラス内の ADAPTER パラメータは EnsLib.File.InboundAdapterOpens in a new tab と一致する必要があります。
-
クラスは、“OnProcessInput メソッドの実装” に記載されているように、OnProcessInput() メソッドを実装する必要があります。
-
その他のオプションと一般情報は、"プロダクションの開発" の “ビジネス・サービス・クラスの定義” を参照してください。
以下の例は、必要となる一般的な構造を示しています。
Class EFILE.Service Extends Ens.BusinessService
{
Parameter ADAPTER = "EnsLib.File.InboundAdapter";
Method OnProcessInput(pInput As %FileCharacterStream,pOutput As %RegisteredObject) As %Status
{
set tsc=$$$OK
//your code here
Quit tsc
}
}
代わりに、予想されるファイルの内容に応じて、OnProcessInput() への最初の引数を %FileBinaryStreamOpens in a new tab にすることができます。
スタジオには、上記のようなビジネス・サービス・スタブの作成に使用できるウィザードが用意されています。このウィザードにアクセスするには、[ファイル]→[新規作成] をクリックし、[プロダクション] タブをクリックします。次に [ビジネス・サービス] をクリックして [OK] をクリックします。このウィザードには、汎用入力引数が用意されています。ウィザードを使用する場合は、このアダプタに必要な特定の入力引数を使用するようにメソッド・シグニチャを編集することをお勧めします。入力引数の型は %FileCharacterStreamOpens in a new tab または %FileBinaryStreamOpens in a new tab にする必要があります。
OnProcessInput() メソッドの実装
ビジネス・サービス・クラス内では、OnProcessInput() メソッドに次のシグニチャを含める必要があります。
Method OnProcessInput(pInput As %FileCharacterStream,pOutput As %RegisteredObject) As %Status
または :
Method OnProcessInput(pInput As %FileBinaryStream,pOutput As %RegisteredObject) As %Status
説明 :
-
pInput は、アダプタがこのビジネス・サービスに送信するメッセージ・オブジェクトです。これは、予想されるファイルの内容に応じて、%FileCharacterStreamOpens in a new tab と %FileBinaryStreamOpens in a new tab のどちらかの型にすることができます。アダプタ設定 ([文字セット]) を使用して、入力ファイルが文字かバイナリかを指定します。"“ファイル受信アダプタに関する設定”" を参照してください。
いずれの場合も、pInput.Attributes("Filename") はファイルの名前と一致します。
-
pOutput は、メソッド・シグニチャに必要な汎用出力引数です。
OnProcessInput() メソッドは、以下の一部またはすべてを実行する必要があります。
-
入力ファイル (pInput) を検査して、その使用方法を決定します。
-
ビジネス・サービスから送信されることになる要求メッセージのインスタンスを作成します。
メッセージ・クラスの作成方法は、"プロダクションの開発" の “メッセージの定義” を参照してください。
-
要求メッセージの場合は、必要に応じて、入力内の値を使用してそのプロパティを設定します。
-
ビジネス・サービスの適切なメソッドを呼び出して、要求をプロダクション内の宛先に送信します。具体的には、SendRequestSync()、SendRequestAsync()、または (あまり一般的ではない) SendDeferredResponse() を呼び出します。詳細は、"プロダクションの開発" の “要求メッセージの送信” を参照してください。
これらの各メソッドは、ステータス (具体的には、%StatusOpens in a new tab のインスタンス) を返します。
-
必ず出力引数 (pOutput) を設定します。通常、受信した応答メッセージと同じように設定します。この手順は必須です。
-
適切なステータスを返します。この手順は必須です。
アダプタ・メソッドの呼び出し
ビジネス・サービス内では、以下のアダプタのインスタンス・メソッドを呼び出すことができます。各メソッドはアダプタ設定に対応しています。これらのメソッドによって、設定の変更後に調整の機会が与えられます。各設定の詳細は、後述する “ファイル受信アダプタに関する設定” を参照してください。
Method ArchivePathSet(pInVal As %String) As %Status
pInVal は、アダプタがファイルの処理を完了した後に、各ファイルのコピーを格納するディレクトリです。
Method FilePathSet(path As %String) As %Status
path は、ローカル・サーバ上のファイルの検索先ディレクトリです。
Method WorkPathSet(path As %String) As %Status
WorkPath
path は、ファイルの処理中にファイルを格納するローカル・サーバ上のディレクトリです。
アダプタのアーカイブ動作の理解
ビジネス・サービスがプロダクション内の宛先に要求を送信した後、アダプタはその要求をトリガした入力ファイルをアーカイブまたは削除できます。次のテーブルは、さまざまな設定が指定されたアダプタのアーカイブ動作を示しています。
このテーブルを使用して、ご使用の環境に最適な設定の組み合わせを選択してください。例えば、プロダクションでメッセージ・バンク・オペレーションを使用して、ビジネス・サービスからのメッセージ・ボディを追跡する場合、1 つ目のシナリオを使用して、ファイル・ストリームが削除される前に、ファイルの内容がメッセージ・バンクにアーカイブされていることを確認できます。詳細は、"エンタープライズ・メッセージ・バンクの構成" を参照してください。3 つ目と 4 つ目のシナリオは、アーカイブされた入力ファイルを永続的に保持する場合に使用できます。6 つ目のシナリオは、競合状態が生じるため削除される可能性があることにより、入力ファイルの内容には依存していないターゲット・ホストでイベントをトリガする場合に使用できます。
3 つ目と 4 つ目以外のすべてのシナリオで、[メッセージ・ボディを含む] 設定が真に設定されている場合、InterSystems IRIS は、手動またはスケジュールされた削除の際に入力ファイルを削除します。
アダプタがホストに送信されたファイルを名前変更または削除できるのは、メソッドがエラーを返さない場合のみです。
シナリオ | ArchivePath と WorkPath | リクエスト・タイプ | ホストに送信されたファイル | ファイルの場所 |
---|---|---|---|---|
1 | ArchivePath と WorkPath は同じだが、FilePath とは異なる | 非同期 | ArchivePath + ファイル名 (オプションでタイムスタンプ付き) に名前変更された入力ファイル | ArchivePath + ファイル名 (オプションでタイムスタンプ付き) |
2 | ArchivePath と WorkPath の設定なし | 同期 | 入力ファイル |
[サーバから削除] が真の場合、なし [サーバから削除] が偽の場合、入力ディレクトリ
Note:
[サーバから削除] は、ビジネス・サービスをカスタマイズすることによってのみ、偽に設定できます。詳細は、"設定の追加と削除" を参照してください。 |
3 | ArchivePath は FilePath とは異なり、WorkPath の設定はなし | 同期 | 入力ファイル | ArchivePath + ファイル名 (オプションでタイムスタンプ付き) |
4 | ArchivePath は WorkPath とは異なり、WorkPath は FilePath とは異なる | 同期 | WorkPath + ファイル名 (オプションでタイムスタンプ付き) に名前変更された入力ファイル | ArchivePath + ファイル名 (オプションでタイムスタンプ付き) |
5 | ArchivePath の設定はなし、WorkPath は FilePath とは異なる | 同期 | WorkPath + ファイル名 (オプションでタイムスタンプ付き) に名前変更された入力ファイル |
[サーバから削除] が真の場合、なし [サーバから削除] が偽の場合、WorkPath + ファイル名 (オプションでタイムスタンプ付き) |
6 | ArchivePath は FilePath と同じで、WorkPath の設定はなし | 非同期 | 入力ファイル |
[サーバから削除] が真の場合、なし [サーバから削除] が偽の場合、入力ディレクトリ |
ビジネス・サービス・クラスの例
例 1
EnsLib.File.InboundAdapterOpens in a new tab を参照するビジネス・サービス・クラスのコードの例を以下に示します。この例は、以下のように動作します。
-
ファイルにはヘッダがあります。ヘッダ情報が各トランザクションに追加されます。
-
ファイルには多数のトランザクションがあります。
-
ヘッダとトランザクション XML 構造が、LBAPP.Header クラスと LBAPP.Transaction クラスによって定義されます (これは表示されていません)。
-
エラー処理がいくつか表示されていますが、すべてではありません。
-
RejectBatch() メソッドは表示されていません。
-
トランザクションは非同期でビジネス・プロセスに送信されるため、ファイル内の順番どおりに処理されるという保証はありません。
-
トランザクション・オブジェクト全体が各メッセージのペイロードとしてビジネス・プロセスに渡されます。
-
1 つのファイル内のすべてのトランザクションが、1 つの InterSystems IRIS セッションで送信されます。
Class LB.MarketOfferXMLFileSvc Extends Ens.BusinessService
{
Parameter ADAPTER = "EnsLib.File.InboundAdapter";
Method OnProcessInput(pInput As %FileCharacterStream,
pOutput As %RegisteredObject) As %Status
{
// pInput is a %FileCharacterStream containing the file xml
set batch=pInput.Filename // path+name.ext
set batch=##class(%File).GetFilename(batch) // name.ext
// Load the data from the XML stream into the database
set reader = ##class(%XML.Reader).%New()
// first get the header
set sc=reader.OpenStream(pInput)
if 'sc {
do $this.RejectBatch("Invalid XML Structure",sc,pInput,batch)
quit 1
}
do reader.Correlate("Header","LBAPP.Header")
if (reader.Next(.object,.sc)) {set header=object}
else {
if 'sc {do $this.RejectBatch("Invalid Header",sc,pInput,batch)}
else {do $this.RejectBatch("No Header found",sc,pInput,batch)}
quit 1
}
// then get the transactions, and call the BP for each one
do reader.Correlate("Transaction","LBAPP.Transaction")
while (reader.Next(.object,.sc)) {
set object.Header=header
set sc=$this.ValidateTrans(object)
if sc {set sc=object.%Save()}
if 'sc {
do $this.RejectTrans("Invalid transaction",sc,object,batch,tranct)
set sc=1
continue
}
// Call the BP for each Transaction
set request=##class(LB.TransactionReq).%New()
set request.Tran=object
set ..%SessionId="" // make each transaction a new session
set sc=$this.SendRequestAsync("LB.ChurnBPL",request)
}
do reader.Close()
quit sc
}
}
例 2
EnsLib.File.InboundAdapterOpens in a new tab を使用するビジネス・サービス・クラスのコードの例を以下に示します。コードのコメントは、OnProcessInput() 内のアクティビティを説明しています。
Class training.healthcare.service.SrvFilePerson Extends Ens.BusinessService
{
Parameter ADAPTER = "EnsLib.File.InboundAdapter";
Method OnProcessInput(pInput As %RegisteredObject,
pOutput As %RegisteredObject) As %Status
{
//file must be formatted as set of lines, each field comma separated:
//externalcode,
//name, surname, dateBirth, placeBirth, provinceBirth
//nationality, gender,
//address, city, province, country,
//fiscalCode
//note:
//fiscalCode may be optional
//sso is an internal code so must be detected inside InterSystems IRIS Interoperability
//operation must be detected as well:
//if the group: name, surname, dateBirth, placeBirth, provinceBirth
//point to a record then it's an UPDATE; if not it's a NEW
//no DELETE via files
Set $ZT="trap"
set counter=1 //records read
while 'pInput.AtEnd {
set line=pInput.ReadLine()
set req=##class(training.healthcare.message.MsgPerson).%New()
set req.source="FILE"
set req.externalCode=$piece(line,",",1)
set req.name=$piece(line,",",2)
set req.surname=$piece(line,",",3)
set req.dateBirth=$piece(line,",",4)
set req.placeBirth=$piece(line,",",5)
set req.provinceBirth=$piece(line,",",6)
set req.nationality=$piece(line,",",7)
set req.gender=$piece(line,",",8)
set req.address=$piece(line,",",9)
set req.city=$piece(line,",",10)
set req.province=$piece(line,",",11)
set req.country=$piece(line,",",12)
set req.fiscalCode=$piece(line,",",13)
//call the process
//res will be Ens.StringResponse type message
set st=..SendRequestAsync(
"training.healthcare.process.PrcPerson", req)
if 'st
$$$LOGERROR("Cannot call PrcMain Process for Person N°" _ counter)
set counter=counter+1
}
$$$LOGINFO("Persons loaded : " _ (counter - 1))
Set $ZT=""
Quit $$$OK
trap
$$$LOGERROR("Error loading for record N°" _ counter _ " - " _ $ZERROR)
SET $ECODE = ""
Set $ZT=""
Quit $$$OK
}
}
例 3
EnsLib.File.InboundAdapterOpens in a new tab を参照するビジネス・サービス・クラスのコードの例を以下に示します。
Class EnsLib.File.PassthroughService Extends Ens.BusinessService
{
Parameter ADAPTER = "EnsLib.File.InboundAdapter";
/// Configuration item(s) to which to send file stream messages
Property TargetConfigNames As %String(MAXLEN = 1000);
Parameter SETTINGS = "TargetConfigNames";
/// Wrap the input stream object in a StreamContainer message object and
/// send it. If you move the input file to the ArchivePath or delete the file
/// after sending, send the message object synchronously. Doing so prevents
/// a race condition, that is, a situation where the adapter attempts to
/// delete or modify the file while the target Config Item is still processing it.
/// Alternatively, send the object asynchronously.
Method OnProcessInput(pInput As %Stream.Object,
pOutput As %RegisteredObject) As %Status
{
Set tSC=$$$OK, tSource=pInput.Attributes("Filename"),
pInput=##class(Ens.StreamContainer).%New(pInput)
Set tWorkArchive=(""'=..Adapter.ArchivePath)&&(..Adapter.ArchivePath=
..Adapter.WorkPath || (""=..Adapter.WorkPath &&
(..Adapter.ArchivePath=..Adapter.FilePath)))
For iTarget=1:1:$L(..TargetConfigNames, ",")
{
Set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W")
Continue:""=tOneTarget
$$$sysTRACE("Sending input Stream ...")
If tWorkArchive {
Set tSC1=..SendRequestAsync(tOneTarget,pInput)
Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
} Else {
Set tSC1=..SendRequestSync(tOneTarget,pInput)
Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
}
}
Quit tSC
}
}
この例では、tSource 変数を、受信ストリーム (pInput) が持つ Attributes プロパティの Filename 添え字に格納されている元のファイル名に設定します。
非同期の要求を送信するのは、入力ファイルを移動または削除するつもりがない場合のみにすることをお勧めします。詳細は、"アダプタのアーカイブ動作の理解" を参照してください。
ビジネス・サービスの追加と構成
ビジネス・サービスをプロダクションに追加するには、管理ポータルを使用して以下の操作を行います。
-
ビジネス・サービス・クラスのインスタンスをプロダクションに追加します。
-
ビジネス・サービスを構成します。設定の詳細は、“設定の参照先” を参照してください。
-
ビジネス・サービスを有効化します。
-
プロダクションを実行します。