クリアフィルター
記事
Mihoko Iijima · 2023年9月26日
この記事では、2023年3月1日~31日の期間に開催された「技術文書ライティングコンテスト:InterSystems IRISチュートリアル」に応募された24作品の中から、Open Exchangeに公開されている sqlalchemy-iris を利用してPythonとSQLでIRISのデータを操作する方法を投稿された Heloisa Paivaさんの記事をご紹介します。
Open Exchangeは、世界各地にいる開発者コミュニティメンバーが開発したインターシステムズ製品で利用できるサンプルアプリが登録されているページで、自由にダウンロードしてご利用いただけます。
Heloisaさんの記事のように公開されている Open Exchange の利用例があると、「ちょっと使ってみたいな・・」と思われているメンバーの方への情報共有ができてとても有用な記事になると思います!丁度日本で初開催の「技術文書ライティングコンテスト」開催中ですので、ぜひ使用例や感想など、投稿してみてください!💪
はじめに
過去の記事で、IRISとPythonの型に関する記事を投稿しましたが、オブジェクトにアクセスする方法は簡単ではないことが明らかです。
幸運なことに、Open Exchange に公開されている SQLAlchemy-iris を利用すればオブジェクトにアクセスするために必要な作業が既に完了しているので、PythonからIRISのオブジェクトへのアクセスがより簡単になりました。
この記事では、SQLAlchemy-iris の利用例をご紹介します。
SQLAlchemy-iris を開発された @Dmitry Maslennikov さん、ありがとう!
インストール方法
管理者権限でターミナルを開き、次のように入力します。
pip install sqlalchemy-iris
必要に応じて、前提条件となる機能もインストールされます。
使用方法
これで、Pythonファイルにモジュールをインポートし、データベースに接続し、好きなようにsqlalchemyを利用できます。よろしければ以下お試しください。
sqlalchemyから "create_engine" をインポートし、IRISへの接続文字列 "iris://username:password@IP:port/namespace" を利用してエンジンオブジェクト(engine)を作成します。もちろん、モジュール全体をインポートすることもできますが、 "create_engine" では、Engine (sqlalchemy.engine :詳細は こちら) のインスタンスを作成し、以下のご紹介する作業で使用する必要なサブクラスをすべて含んでいます。
from sqlalchemy import create_engine
engine = create_engine("iris://_SYSTEM:SYS@localhost:1972/SAMPLE")
接続( sqlalchemy.engine.connection:詳細は こちら)インスタンスを作成します。このインスタンスを使用してトランザクションや単純な実行などが行えます。
conn = engine.connect()
素晴らしい!これでデータベースへのアクセス設定は完了です。
シンプルなSELECT文を実行します。結果セットを繰り返し処理するには、次のようにします(例として、別の記事で作成したテーブルを利用しています)
query = 'SELECT Name, Age from Sample.PersistentData WHERE Age >=21'
result = conn.exec_driver_sql(query)
この実行で、result には、CursorResult(sqlalchemy.engine.CursorResult)が設定されます。CursorResultでできることについてはオフィシャルドキュメントで確認できます。
CursotResultを利用したシンプルな繰り返し処理は以下の通りです。
print("Name, Age")
for row in result:
print(row[0], ", ", row[1])
少し書式を変えれば、次のような結果が得られます。
詳細は、オフィシャルドキュメントもぜひご覧ください。
追記:sqlalchemyから"text"をインポートすると、次のようにクエリを実行することもできます。
result = conn.execute(text(query))
上述した結果と同様の結果が得られます。
結論
DDLとその他DML文も実行できますし、ORM(Object Relationalマッピング)と連携するためのサポートも充実しています。この記事では簡単な利用例をご紹介するだけでしたが、この他の利用例については、ぜひ皆さんからの投稿をお待ちしてます!
記事
Mihoko Iijima · 2023年9月13日
この記事では、2023年3月1日~31日の期間に開催された「技術文書ライティングコンテスト:InterSystems IRISチュートリアル」に応募された24作品の中から、Heloisa Paivaさんが投稿されたシンプルですぐに試せる記事をご紹介します。
はじめに
このチュートリアルは、テストやチュートリアル用のサンプル作成など、あらゆる目的でサンプルデータベースを作成するための、私が見つけた最も簡単な方法についての簡単なチュートリアルです。
ネームスペースの作成
ターミナルを開きます。
次のコマンドを実行します。 "Do $SYSTEM.SQL.Shell()" (※または :sql の入力でもSQLシェルに切り替えできます)
"CREATE DATABASE " コマンドを実行します。実行時、作成したいネームスペース名をコマンドの引数に指定します。(TESTネームスペースを作成する例:CREATE DATABASE TEST)
これで、管理ポータルから新しいネームスペースを作成するより簡単で素早い方法でネームスペースを作成できます。
ネームスペースに移動するには、現在開いているSQLシェルを終了させるため、"quit" を入力し、zn "ネームスペース名" と入力します。これでターミナルで行うすべての動作がこのネームスペースのスコープに入ります。
テーブルの作成
ターミナルを開きます。
次のコマンドを実行します。 "Do $SYSTEM.SQL.Shell()" (※または :sql の入力でもSQLシェルに切り替えできます)
Enterを入力し、SQLシェルを複数行モードに切り替えます。
以下例のようなコマンドを実行します。
[SQL]SAMPLE>> << entering multiline statement mode, 'GO' to execute >>
1>>CREATE TABLE Sample.PersitentData (
2>>Name %String,
3>>Age %Integer )
4>>GO
IRISのタイプ(%Stringなど)またはSQLの型(VARCHARなど)でプロパティを指定することができます。CREATE TABLE (SQL)にあるように、このコード内で多くの設定を定義することもできますが、この記事の目的から、できる限りすべてをシンプルに記述しています。
これで、管理ポータル、スタジオ、VS Code、またはターミナルから、例で作成したパッケージ名、クラス名「Sample.PersistentData」でアクセスできます。
データの自動生成
テーブルの作成が完了したら、スタジオやVSCodeからクラス定義にアクセスし、"Extends"パラメータに%Populateを追加します。
クラス定義文は以下のようになります。
Class Sample.PersistentData Extends (%Persistent, %Populate)
この他のスーパークラスとして、%JSON.Adaptor と %XML.Adaptor は、データのエクスポート、プロダクション・ポータルでの表示などに便利です。
これでターミナルを開き、以下のコードを実行できるようになりました:
D #class(Sample.PersistentData).Populate(100)
Ageに関して、Populate()メソッドはランダムな整数を作成できますが人の適切な年齢であるかどうかは気にしていません。
以下、実行結果です。
テーブルを作るときにもっと複雑なコードを作ることもできますが、私はできるだけシンプルな方法ですべてを実施したため、以下のコードを実行しました:
UPDATE Sample.PersistentData
SET Age = Age # 120
結論
このチュートリアルはそれほど小さなものではないように見えるかもしれませんが、私はこれらのステップをすべて学んだ後、自分の会社に持ち込みたいアイデアをテストしたり、ここに書く記事のサンプルを作成したりするのに使っています!( もしかしたらあたなは私が書いた内容を既にご存知かもしれませんね!)
この手順を利用すると、適切に構造化されたデータベースを持つことができるのと、コンテンツ自体の開発を始める前の手続きとして1分程度の時間しかかかりません。
記事
Toshihiko Minamoto · 2023年1月24日
ZPM は、InterSystems IRIS データプラットフォーム用のアプリケーションやモジュールと連携するように設計されています。モジュールを管理するためのCLIであるZPN Clientと、モジュールやメタ情報のデータベースであるThe Registryの2つのコンポーネントで構成されています。ZPM を使用して、モジュールの検索、インストール、アップグレード、削除、公開を行うことができます。ZPMを使用すると、ObjectScriptクラス、フロントエンドアプリケーション、Interoperabilityプロダクション、IRIS BIソリューション、IRISデータセット、またはEmbedded Pythonホイールなどのあらゆるファイルをインストールできます。
今日、このクックブックは3つのセクションについて説明します。
1. ZPMのインストール
2. モジュールの作成
3. Registry内のモジュールの検索、インストール、公開
1. ZPMのインストール
* ZPM最新バージョンのダウンロード(1つのXMLファイルです)[ダウンロードリンク](https://pm.community.intersystems.com/packages/zpm/latest/installer)
* ダウンロードしたXMLをIRISにインポートし、IRISターミナルを開き、次のようにコマンドを入力するだけでIRISにデプロイされます。
_write $SYSTEM.OBJ.Load("C:\zpm.xml", "c")_
注意:"C:\zpm.xml"は、ダウンロードしたXMLファイルのパスです。このステップはしばらく時間がかかるかもしれません。
* インストールが完了したら、_zpm_ とタイプして Enter キーを押すと、zpm シェルが表示されます。

2. モジュールの作成
モジュールの作成を始める前に、ダウンロード用のファイルを格納したフォルダを用意する必要があります。そこで、zpm というフォルダをC ドライブに 作成しました。
コマンド _generate C:/zpm_ を実行します。
必要な項目をすべて指定すると、最初のモジュールが正常に生成され、次のような画面も表示されます。

注意:
1. モジュールのバージョン(module version)はセマンティックバージョニングを使用しています
2. モジュールソースフォルダ(module source folder)は、すべてのクラスファイルが格納されているフォルダです。
3. zpm にはウェブアプリケーション(Web application)と依存関係(Dependencies)を追加するオプションもありますが、この例では空白にしておきます。
ここで、ファイルエクスプローラを開くと、以下のスクリーンショットのように、「module.xml」というファイルが表示されるはずです。

コマンド "_load C:\ZPM__" をタイプします。タイプすると、モジュールが再ロードされ、検証され、コンパイルされ、アクティブ化されます。

3. Registry内のモジュールの検索、インストール、公開
最新のRegistryで利用可能なパッケージを検索します: _zpm:USER>search_
最新のRegistry からパッケージをインストールする 例として、zpmshowというモジュールをパブリックRegistryにインストールする方法を説明します: _zpm:USER>install zpmshow _(コマンドは、 "moduleName" をインストールします)。
ロード後、モジュールを公開します: _zpm:USER>publish myFirstZPMDemo_
_zpm:USER>search_ を使って公開を確認することができます。この場合、"myfirstzpmdemo 0.1.0 "が最新のRegistryにあることがわかります。

注意:モジュールを公開しているときに、次のようなエラーが発生した場合:_ "エラー! 発行モジュールにおいて、何か問題が発生しました(ERROR! Publishing module, something went wrong)" と表示される場合、_最新のRegistry のステータスが有効で利用可能であることを確認してください。
zpm:USER>repo -list, _で最新のRegistryの状態を確認することができます。

ビデオ:[こちらをクリックしてください](https://www.loom.com/share/0ca097f0dea4476ea294841295f972b2%C2%A0%C2%A0)
記事
Toshihiko Minamoto · 2021年10月20日
インターネットを使うようになってから (1990年代後半)、PythonとIRIS グローバルを使ってブログを書いていますが、常にCMS (コンテンツ管理システム) でブログ、ソーシャルメディア、さらには企業ページに情報を簡単に投稿できるようにしていました。
数年後、自分がマークダウンファイルに収めて使ってきたすべてのコードをgithubに入れました。
ネイティブAPIでデータをIntersystems IRISに入れて永続化するのはとても簡単だったので、このアプリケーションを作成して少しSQLを忘れ、キー・バリュー・データベースモデルを受け入れることにしました!

## ブログとは?
これはWEB LOGの略名で、基本的にはユーザーが書いてページに投稿、公開できるプラットフォームです。
## マークダウンフォーマットとは?
マークダウンとは、フォーマットしたテキストを作成するマークアップ言語であり、HTMLより簡単で、多くのCMSプラットフォームで人気があります。
例:
```
# Header 1
## Header 2
### Header 3
```
結果:
# Header 1
## Header 2
### Header 3
***********
## IRISで作成するメリットとは?
IRIS Globalsを使用して各投稿のデータを永続化しながら、各データコンサルタントはキー・バリュー・データベースとして動作するIRISのスピードというメリットを得ます。すでに作業でIris Instanceを使用している場合は、このようなアプリケーションを作成するために別の技術を使用する必要はありません。
## そしてPythonとは?
Pythonは最も人気の高いプログラミング言語の一つです。チューリング完全言語であり、開発課題のほとんどを楽にしてくれるオープンソースライブラリがたくさんあります。
私は、PythonとIrisを統合するために、IRISNative APIを使用しました。
## データベースモデル キー値
私のエンジンは、これでもかというほどシンプルに動作します。
各投稿を永続化するために、サブスクリプトが「post」、その次のサブスクリプトが投稿IDのグローバル「^blog」を作成します。
このグローバルに投稿コンテンツを入れて、終わりです!
これだけで、テーブル、インデックスなどを作成する必要はありません。
```
^blog("post", "1") = "# post 1 content..."
^blog("post", "2") = "# post 2..."
^blog("post", "3") = "# post 3 markdown content..."
```
## HTMLでマークダウンをレンダリングするには?
これで、オープンソースモジュール使用可能なPythonのメリットを無限に得るこquoとができます。私の選択は、たった1行のコマンドで簡単にマークダウンをレンダリングできるDashライブラリでした(^_^)
```python
import dash_core_components as dcc
import dash
import irisnative
#creating a connection with an IRIS Instance
conn = irisnative.createConnection("host","port","namespace","username","password") obj_iris = irisnative.createIris(conn)
#getting the content of one post with id 1
content = obj_iris.get("blog", "post", "1")
#creating the dash application
app = dash.Dash(__name__)
#rendering a markdown value
rendered_markdown_in_html = dcc.Markdown(content)
#showing on the page the rendender markdown
app.layout = html.Div([rendered_markdown_in_html])
```
## すべての投稿を表示するには?
サブスクリプト「^blog("post",)」繰り返して、上記と同じ方法でレンダリングされたマークダウンをページにプリントできます。
データベースをモデル化し、これよりも速く簡単に動作するフォームを作ったことがありますか?回答はコメント欄に書いてください!
## 自分でビルドしたくないけど、動作する様子は見たい!
簡単です!ここをクリックして、*御覧ください。*
http://iris-multimodel-suite.eastus.cloudapp.azure.com/blog-post
(サイトは既に閉鎖されています。ご了承ください)
# この記事とアプリケーションをお楽しみいただけましたか?
ここではマルチモデルコンテストでの私のアプリケーションの一部をご説明しています。よろしければ、私のアプリに投票してください。
記事
Megumi Kakechi · 2024年7月23日
これは InterSystems FAQ サイトの記事です。
JDBC および ODBC 経由でInterSystemsIRISから外部データベースにアクセスしたい場合、SQLゲートウェイを使用しリンクテーブルを作成して接続できます。
2023.1以降のバージョンでは、リンクテーブルに加えて、外部テーブル/FOREIGN TABLE を使用することが可能となりました(2024.1時点で実験的機能)。
外部テーブルというのは、物理的に別の場所に保存されているデータを IRIS SQL に投影する非常に便利な機能です。外部テーブルを使用する場合は、Java(2023.1の場合は1.8~)を事前にインストールし、JAVA_HOME環境変数を設定するだけで、簡単に接続することが可能です。
※JAVA_HOME環境変数設定例:
外部テーブルの使用方法については、以下の記事で紹介しております。レシピデータセットを外部テーブルで読み込み、組み込みPythonでLLMを使って分析する (Langchain + OpenAI)
こちらの記事では、外部テーブルで作成できる2種類のテーブル(「CSVファイル直接接続」と「外部DBへのJDBCゲートウェイ経由での接続」)の簡単なサンプル作成例と、外部テーブルの特徴を紹介しています。
1-1. 簡単なサンプル作成例(CSVファイル編:ファイルから外部テーブル作成)
a. 外部データラッパとする CSVファイルを用意します(例:C:\temp\FT\managers.csv)
※サンプルCSV(managers.csv)
ID,Name,Title,HireDate,CompanyCar
111,"Cornish,Irving",Senior Support Manager,1992-02-10,6
222,"Aquino,Aric","Manager, Technical Account Management",1992-07-15,3
333,"Masterson,Arthur","Director, Customer Support",2002-10-01,9
444,"Deyn,Ernest",Director Customer Support,2000-08-15,4
555,"Lee,Eileen","Manager, Product Support",2002-06-17,3
666,"Knapp,Ashtyn",Senior Support Manager,2002-10-01,11
777,"King,Michael",Senior Support Manager,2003-04-10,2
b. 外部サーバ(WRC.Files)を作成します
CREATE FOREIGN SERVER WRC.Files FOREIGN DATA WRAPPER CSV HOST 'C:\temp\FT\'
c. 外部テーブルを作成します
CREATE FOREIGN TABLE WRC.Managers (
ID INTEGER,
Name VARCHAR,
Title VARCHAR,
HireDate DATE
) SERVER WRC.Files FILE 'managers.csv' USING
{ "from" : {
"file" : {
"header": 1
}
}
}
1-2. 簡単なサンプル作成例(JDBCゲートウェイ接続経由編)
a. 外部DBへの JDBCゲートウェイ接続を作成します 管理ポータル: [システム管理] > [構成] > [接続性] > [SQLゲートウェイ接続] 新規作成:WRC
b. 接続用の外部サービス(例:WRC.Data)を作成します。
CREATE FOREIGN SERVER WRC.Data FOREIGN DATA WRAPPER JDBC CONNECTION 'WRC'
c. 外部サーバ内の任意のテーブルに対して外部テーブルを作成します。 ※外部テーブルは CREATE FOREIGN TABLE コマンドで定義する必要があります。 クラス定義を作成して外部テーブルを作成することはできません。
CREATE FOREIGN TABLE Remote.Problems SERVER WRC.Data TABLE 'SQLUser.Problem'
d. 作成後、クエリを実行します。
SELECT ProblemOwner, OpenDate FROM Remote.Problems WHERE OpenDate = '2023-03-09'
外部言語サーバ(%Java Server)が起動されていない状態でクエリを実行すると、以下のようなエラーが返ります。
SQLCODE: <-230>:<Foreign table query Execute() failed>]
[%msg: <Foreign Tables - ERROR #5023: Remote Gateway Error: Connection cannot be established>]
2. 外部テーブルの特徴
・外部テーブルとのJoinが可能
・ローカルテーブルとのJoinが可能
・外部テーブル用に作成されるクラスは非表示となる(SQLテーブルとしては表示可能)
・削除する場合は、DROP FOREIGN TABLE コマンドで行う(%MANAGE_FOREIGN_SERVER 管理特権が必要) 例:DROP FOREIGN TABLE WRC.Advisor
・外部テーブルに対してクエリを実行すると、クエリごとにすべてのフィールドが取得される
・ストリーム(Stream)フィールドの取得方法は、リンクテーブルと同様に substring 関数 を使用可能
例:
select substring(clob1,1,50) from linked.newclass1
※うまく動作しない場合は、%Java Server が問題なく起動できているかご確認ください。 [システム管理] > [構成] > [接続性] >[外部言語サーバ] %Java Server が Start されているか
外部テーブルの詳細については、以下のドキュメントをご覧ください。外部テーブル※SQLゲートウェイ/リンクテーブルの使用方法については、以下のような記事をご紹介しております。
(管理ポータルで行う)リンクテーブルをプログラムで行う方法SQL ゲートウェイを使用した外部データベースへのアクセス方法についてプログラムでSQLゲートウェイ接続設定を作成する方法
記事
Megumi Kakechi · 2025年2月17日
これは InterSystems FAQ サイトの記事です。
1. 整合性チェックの目安の時間
整合性チェックの目安の時間は、環境により異なってきます。ディスク速度やDBサイズに大きく依存してきます。
同じディスク上に置かれた比較的サイズの小さなDATでまず実施して、サイズと経過時刻からおおよその時刻を推測してください。
また、整合性チェックの方法によって要する時間が変わってきます。
詳細は参考記事で紹介しております「整合性チェックの各方法の違いについて」をご覧ください。
2. 動作状況(正常に稼働しているか)を確認する方法
ターミナルで整合性チェックを実行していただく場合は、現在どのデータベース/グローバルのチェックを実行しているのかを出力ログ(画面またはファイル)にて確認することが可能です。
管理ポータルで実行する場合は、整合性チェックの実行プロセスID(※PIDの確認方法)を指定して、ターミナルで以下のコマンドを実行することにより確認することが可能です。詳細については、ドキュメント をご覧ください。
%SYS>do Display^Integrity("^IRIS.TempIntegrityOutput(2596)") // 整合性チェック実行プロセス(2596)の場合
対象プロセス(PID)のプロセス詳細より、グローバル参照や実行されたコマンドで、稼働しているかを確認することも可能です。
PIDの確認は、管理ポータルから実行している場合は、バックグラウンドタスクより整合性チェックの実行プロセスIDを確認します。
[システムオペレーション] > [バックグラウンドタスク] ※タスク名:データベース整合性チェック、PIDを確認。 タスクスケジュールで実行の場合は、バックグラウンドタスクで確認することはできません。
実行中のプロセスが動作しているかは、対象プロセスがRead I/O を発行しているかでも確認できます。Linuxの場合、プロセスのRead I/Oの監視は、iotop コマンドが使用可能であれば、以下のコマンドで確認できます。※iotop:どのプロセスがI / Oが進行中であるかを示すために使用します
# iotop -p <pid>
iotop コマンドが使用できない場合は、/proc/<pid>/io をviコマンド等でご覧いただくと read_bytes にて確認できます。
Windowsの場合は、Windowsリソースモニタを使用します。
起動方法は以下のいずれかの方法になります。
タスクマネージャーの[パフォーマンス]タブにある[リソース モニター]ボタン
コントロールパネル > 管理ツール > リソースモニタのショートカット
perfmon.exe /res
3.途中で止める方法
管理ポータルのプロセス詳細より、対象プロセスを停止します。[システムオペレーション] > [プロセス] 上記プロセスIDの 詳細 を開き、終了 ボタンで終了します。 ターミナルで^Integrity ユーティリティを使用している場合は、Ctrl+Cで停止します。
ただし、大きなグローバルをチェックしている最中ですと、上記方法では停止できません。
その場合は、^JOBEXAMユーティリティを使用して強制的にプロセスを終了させる必要があります。【手順】別ターミナルで ^JOBEXAMを実行し、該当プロセスをTerminateします。
%SYS>do ^JOBEXAM
IRIS for Windows (x86-64) 2024.1.1 (Build 347U)
Job# NSpace Routine Commands Globals State PID Current device
:
18 %SYS %SYS.WorkQueueMgr 30484 839 EVTW 2596 //./nul ← 停止したいプロセス
(N)ext,(P)rev,(G)oto,(E)xamine,(T)erminate,(S)uspend,(R)esume,(Q)uit => ← T を入力+Enter
Terminate: Enter Job # or "P" followed by the PID: ← 上記サンプルの場合 P2596 を入力+Enter
【ご参考】^Integrity (^INTEGRIT) ユーティリティの使用方法整合性チェック: 高速化と低速化整合性チェックの各方法の違いについて
記事
Hiroshi Sato · 2025年3月4日
これは InterSystems FAQ サイトの記事です。
計算プロパティを定義する際に利用可能なキーワードが複数あります。
詳細は、以下をご参照ください。
計算プロパティの定義
実際のこれらのキーワードの関連性は、少々複雑ですので具体的なコードを作成して動作を確認してみます。
以下のようなクラス定義を作成します。(プロパティとインデックス定義のみ表示します)
完全なクラス定義は以下より、ダウンロードできます。
サンプルクラス定義
Class Sample.Person Extends %Persistent [ ClassType = persistent, ProcedureBlock ]
{
Property FirstName As %String;
Property LastName As %String;
Property DOB As %Date;
Property Age1 As %Integer [ Calculated, SqlComputed, Transient ];
Property Age2 As %Integer [ Calculated, Transient ];
Property Age3 As %Integer [ Calculated, SqlComputed ];
Property Age4 As %Integer [ Calculated ];
Property Age5 As %Integer [ SqlComputed, Transient ];
Property Age6 As %Integer [ Transient ];
Property Age7 As %Integer [ SqlComputed ];
Property Age8 As %Integer [ SqlComputed, SqlComputeOnChange = DOB ];
// インデックスは、Trancient = true の時には作れない
// Index IndexAge1 on Age1;
// Index IndexAge2 on Age2;
// Index IndexAge5 on Age5;
// Index IndexAge6 on Age6;
Index IndexAge3 On Age3;
// インデックスは、SqlComputed = false の時には作れない
// Index IndexAge4 On Age4;
Index IndexAge7 On Age7;
Index IndexAge8 On Age8;
}
計算プロパティとしてAge(年齢)というプロパティを作ります。
年齢は、今日の日付から誕生日(DOB)を引き算して経過日数を求め、閏年を考慮しつつ年の日数で割り算することで求められます。
以下にキーワードの組み合わせによる違いについて表にまとめています。
同じような年齢プロパティがこれらのキーワードの組み合わせに基づき、8個定義されています。
計算プロパティは、このキーワードの組み合わせによりインデックスを設定できる場合があります。
そして上のコードのコメントに書いてあるとおり Trancient = trueおよびSqlComputed = falseが設定されているとインデックスを作ることができません。 従ってこのサンプルでは、Age3, Age7, Age8のみがインデックス作成可能です。
またAge1とAge2はインデックスが作成できない点で共通で、違いはSqlComputedの指定だけですが、結果がどのように計算されるかについて大きな違いがあります。
それではその計算がどの様にどのタイミングで行われるかを示すサンプルコードを実行してみます。
上のコードに予め含めておいたクラスメソッドを実行することで動作を確かめてみます。
USER>do ##class(Sample.Person).CalculatedPropertyTest()
^Sample.PersonD=1
^Sample.PersonD(1)=$lb("","Kaoru","Shinuchi",60000,18,18)
^Sample.PersonI("IndexAge3",18,1)=""
^Sample.PersonI("IndexAge7",18,1)=""
^Sample.PersonI("IndexAge8",18,1)=""
ID Age1 Age3 Age5 Age7 Age8 DOB FirstName LastName
1 18 18 18 18 18 60000 Kaoru Shinuchi
1 Rows(s) Affected^Sample.PersonD=1
^Sample.PersonD(1)=$lb("","Kaoru","Shinuchi",50000,18,46)
^Sample.PersonI("IndexAge3",46,1)=""
^Sample.PersonI("IndexAge7",18,1)=""
^Sample.PersonI("IndexAge8",46,1)=""
ID Age1 Age3 Age5 Age7 Age8 DOB FirstName LastName
1 46 46 46 18 46 50000 Kaoru Shinuchi
1 Rows(s) Affected
Age1の場合、このキーワードの組み合わせでは、プロパティの計算タイプは常に計算になります。
そしてTrancientなのでデータの実体は持ちません
Age2の場合、プロパティの計算タイプは、計算されないになります。そしてTrancientなのでデータの実体は持ちません。
さらにsqlComputedがFalseなのでSQLのフィールドとしても認識されません。
(但し、このサンプルコードでは示していませんが、クラスインスタンスの計算プロパティとしてはデータ取得可能です。)
Age3の場合、プロパティの計算タイプは、常に計算になります。
データの実体は持たず、その計算結果に基づきインデックスを生成します。
そしてデータが更新された場合にもその更新内容に基づきインデックスも更新されます。
Age4, 6はAge2と同様です。
Age5は、Age1と同じです。
Age7の場合、プロパティの計算タイプは、トリガーによって計算になります。
しかし、SqlComputeOnChangeが定義されていないので、DOBが変更されてもその変更が検知できずに初回に設定された値をずっと保持したままです。
Age8の場合、プロパティの計算タイプは、トリガーによって計算になります。
ここでは、SqlComputeOnChangeが定義されているので、DOBが変更されるとその変更に基づきAgeが再計算されます。
記事
Hiroshi Sato · 2020年7月2日
初めに
VisM.OCXはVisual Basicでクライアント・サーバー型のアプリケーション開発を支援するためにInterSystemsが提供してきたツールです。
誕生から既に20年以上が経過した非常に古いテクノロジーです。
OCX規格(ActiveXコンポーネント)は、マイクロソフト社が推進してきた規格ですが、やがてマイクロソフト社が後継となる.Net Frameworkをリリースし、その新しいフレームワークへの移行を強力に推進すると同時に、OCX規格は非推奨機能となっています。
一方で下位互換性のため、.Net Framework配下でOCXを動作可能とする仕組みが用意されており、結果としてOCXは、.Net Framework環境下で動作可能です。
従って、VisM.OCXも.Net対応のプログラミング言語C#やVB.NETからそのまま利用することができます。
しかしながら.Net Framework上で動作するとは言え、.Net Frameworkが用意する安全性の高い資源管理や強固なセキュリティ機能の恩恵を受けることができず、いわゆるマネージドコードとして動作できないという制約を抱えています。
上記の状況を整理した結果、InterSystems社は、IRISをリリースするに際し、VisM.OCXは、標準製品では動作せずに、特別なキーを適用することで動作可能になるような措置(使用を制限する)を適用しました。
その結果、VisM.OCXを使用するためには、InterSystems社にお問い合わせいただき、その特別なキーを取得するための申請を行う必要があります。
また広く皆様にIRISを使って評価していただきたいと考えて提供している無償のCommunityエディションでも動作しません。
ここでIRISでVisM.OCXアプリケーションを動かすのはハードルが高いと感じた方に朗報です。
VisM.OCXを使用せずに、VisM.OCX対応アプリケーションをIRIS上で動作可能にするための移行ツール(Caché Directエミュレーター)を用意しました。
このツールは、Open Exchangeから取得可能です。
Open Exchangeサイト
ここでは、このツールを使った移行作業を実際に簡単なVBアプリケーションを例として紹介していきたいと思います。
ここで説明しているADBKアプリケーションは、下記のGithubから入手可能です。
ADBKサンプル
ADBKアプリケーション
このサンプルアプリケーションは、20年以上前にVB6サンプルとして作成されました。
アドレス帳をCaché データベースに保存し、クライアントのVB6アプリケーションからVisM.OCXを使って、データの登録や検索を行う非常にシンプルなサンプルです。
VB6プロジェクトを.Netプロジェクトに変換する
このVB6のアプリケーションをまず.Netアプリケーションに変換しなければなりません。
この変換を行うためには、最新のVisual Studioではだめで、Visual Studio 2008を別途インストールする必要があります。
変換方法は以下のサイトをご参考ください。
VB6アプリケーションをVB.NETに変換する方法
VisM.OCXの削除
次にその変換された.net プロジェクトファイルを新しいVisual Studio(現時点では2019が最新)で読み込みます。
まずメインフォームの右下に配置されているVisM.OCX(青い立方体のアイコン)が見えると思います。
この部品を削除します。(選択した後に、右クリックで削除メニュー項目が表示される)
次に右側に表示されているソリューションエクスプローラーの参照の所を開いて、AxVISMLibを選択して右クリックし、削除を選びます。
同様にその下のVISMLibを選択して右クリックし、削除を選びます。
必ず部品の削除を先に行ってください。
ここで更新したファイルの保存を行い、プロジェクトを閉じます。
必要に応じて、この状態一式を別ディレクトリーにバックアップします。
C#エミュレータークラスを使用可能にする
Caché DirectエミュレータをOpen Exchangeからダウンロードし、そのZipファイルを適当なディレクトリに展開します。
cacheDirectWapper.csを新規のC# クラスライブラリープロジェクトに取り込みます。
参照設定でIRISの.Netライブラリーを指定します。
c:\intersystems\IRIS\dev\dotnet\bin\v4.6.2InterSystems.Data.IRISClient.dll
プロジェクトメニューからxxxのプロパティをクリックします。
xxxはプロジェクト名です。左側のペインからアプリケーションを選びます。
アセンブリ名は適当な名前にします。(CacheDirectEmulatorなど)対象のフレームワークが.Net Framework 4.8でなければ、.Net Framework 4.8に変更します。
左側のペインからビルドを選びます。構成をアクティブな(Release)またはReleaseにします。
ビルドします。(dllが作成されます)
Caché Directエミュレーターの参照設定を追加する
VB.Netのプロジェクトを再度開き、プロジェクトメニューから参照の追加を選択します。
作成されたdllを参照設定します。(dllとアプリケーションプロジェクトの.Net Frameworkのバージョンを合わせる必要があります)
アプリケーション修正
それでは、VisM.OCX用に作られたアプリケーションをCaché Directエミュレータのインタフェースを使って動作するように変更しましょう。
まず、ADBKMain.vbを開きます。
まず、新しく追加したCaché Directエミュレータライブラリーを参照できるようにインポートする必要があります。
これは先頭にあるOption文の後ろに以下のように追加します。
Option Strict OffOption Explicit On'以下のインポートが必要Imports cdapp
続いてVisM.OCXの置き換えです。
このVBアプリは、直接VisM.OCXを使用するのではなく、それをラップするクラスを作成していました。従って、ここでは、VisM.OCXを直接置き換えるのではなく、そのラッパークラスを置き換える方式を採用します。
元々の定義は以下のようになっていました。
cVMClassというのが、VisMをラップしたクラスです。
Dim CacheDirect As New cVMClass
これをCaché Directエミュレータクラスを参照するように変更します。
長いので途中で折り返しています。
Public WithEvents CacheDirect As cacheDirectWapper = New cacheDirectWapper("Server = localhost; Port=1972; Namespace=USER; Password = SYS; User ID = _system;")
VisM.OCXの場合は、サーバーへの接続は、いくつかオプションがあるのですが、このエミュレーターはオブジェクト生成時にサーバー接続まで行う仕様になっています。
WithEvents句は、イベント処理が不要の場合は、指定しなくてもいいです。
これでCacheDirectを参照しているところは、ほとんど修正することなくそのまま使用できます。
但しフォームのロード処理にいくつか変更が必要でした。
Private Sub ADBKMain_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load CacheDirect.VisM = VisM1.GetOcx If Not Install("Address Book Demo Application", VisM1, "GLO", "^ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.gsa", "MAC", "ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.rsa") Then End End Sub
ここで元々のラッパークラスでは、VisM.OCXをプロパティとして設定する仕様だったのですが、これは今回必要なくなりました。
次にデータやルーチンが初期インストールされていない場合にそれらをロードする仕組みを用意していたのですが、これは今はちゃんとライブラリーが用意されていて、それを使うほうが簡単なので、書き換えることにします。
Private Sub ADBKMain_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load 'CacheDirect.VisM = VisM1.GetOcx 'If Not Install("Address Book Demo Application", VisM1, "GLO", "^ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.gsa", "MAC", "ADBK", "USER", "+'$D(^$ROUTINE(""ADBK""))", "adbk.rsa") Then End
CacheDirect.Execute("=$DATA(^$ROUTINE(""ADBK""))") If CacheDirect.VALUE = 0 Then CacheDirect.P0 = "c:\temp" CacheDirect.P1 = "ck-d" CacheDirect.Execute("set P2=$system.OBJ.ImportDir(P0,,P1,.P2)") If CacheDirect.P2.Substring(0, 1) <> 1 Then MsgBox("Loadでエラーが発生しました" & CacheDirect.P2)
End If
End If
End Sub
最後にオブジェクトの消滅に関してもより安全な方法を採用したために、書き換えが必要です。
Private Sub ADBKMain_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 CacheDirect = Nothing End Sub
Caché Directエミュレータクラスのendメソッドを呼び出すように変更します。
Private Sub ADBKMain_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 'CacheDirect = Nothing CacheDirect.end() End Sub
続いてFindByName.vbの変更点を見てみましょう。
ここでもCacheDirectのインスタンス化を実施していましたが、今回はADBKMainクラスのCaché Directエミュレータクラスを共有できるので、ここで宣言する必要がなくなります。
以下の記述をコメントアウトします。
'Dim CacheDirect As New cVMClass
次に検索ボタンが押された時の処理を変更していきます。
Private Sub CmdFind_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdFind.Click Dim i As Object 'Dim NOL As Short' 名前のリストを取得 ListLookupName.Items.Clear() '以下の機能は未実装 CacheDirect.Clear() CacheDirect.PDELIM = Chr(1) CacheDirect.P0 = "^ADBK(""XNAME"")" CacheDirect.P1 = TxtSNAME.Text CacheDirect.Execute("Do GetList^VISMUTIL(P0,P1,P1,"" "")") If CDbl(CacheDirect.Error_Renamed) <> 0 Then ' MsgBox (" Error " & CacheDirect.ErrorName) Exit Sub End If ' 取得した名前リストをListBoxに展開 For i = 1 To CacheDirect.NoOfPLISTItem 'UPGRADE_WARNING: オブジェクト i の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 ListLookupName.Items.Add(CacheDirect.PLISTItem(i)) Next iEnd Sub
ここでCacheDirect.Clear()はラッパークラスが独自に用意していたメソッドで、新しいエミュレータクラスには存在しません。
内容を確認した所、P0-P9,PLIST変数を初期化する処理のようで、処理を俯瞰する限り、ここで初期化する必要がないと判断し、コメントアウトすることにします。
ここでは、ADBKMainのエミュレータクラスを参照する必要があるため、すべてのCacheDirect参照の前にADBKMain.を付加する必要があります。
そして、VB6からVB.NETにコンバートする際に、Errorというキーワードは強制的に変換されるようで、Error_Renamedになっていたので、これを元のErrorに戻す必要がありました。
NoOfPLISTItemとPLISTItem(i)はラッパークラス独自の実装だったのですが、機能的には全く同じものがエミュレータークラスにあるので、そのメソッドに名前の変更を行いました。
Private Sub CmdFind_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdFind.Click Dim i As Object 'Dim NOL As Short ' 名前のリストを取得 ListLookupName.Items.Clear() '以下の機能は未実装 'CacheDirect.Clear() ADBKMain.CacheDirect.PDELIM = Chr(1) ADBKMain.CacheDirect.P0 = "^ADBK(""XNAME"")" ADBKMain.CacheDirect.P1 = TxtSNAME.Text ADBKMain.CacheDirect.Execute("Do GetList^VISMUTIL(P0,P1,P1,"" "")") 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then If CDbl(ADBKMain.CacheDirect.Error) <> 0 Then MsgBox (" Error " & ADBKMain.CacheDirect.ErrorName) Exit Sub End If ' 取得した名前リストをListBoxに展開 'For i = 1 To CacheDirect.NoOfPLISTItem For i = 1 To ADBKMain.CacheDirect.getPLISTLength() 'UPGRADE_WARNING: オブジェクト i の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 'ListLookupName.Items.Add(CacheDirect.PLISTItem(i)) ListLookupName.Items.Add(ADBKMain.CacheDirect.getPLIST(i)) Next iEnd Sub
次にイベント処理の変更です。
VisM.OCXでExecuteメソッドが実行されたことを補足するためのイベント処理が以下の様に定義されていました。
'Private Sub VisM1_Executed(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles VisM1.Executed Dim status As Object If VisM1.P9 <> "" Then 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("データエラー " & VisM1.P9, MsgBoxStyle.OKOnly, "データエラー") End If End Sub
Handle以下をエミュレーターのイベントに書き換える必要があります。ここではVisM.OCXを直接参照していたので、その部分をエミュレータークラスを参照する様に変更します。
Private Sub CacheDirect_Executed(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CacheDirect.ExecuteEvent Dim status As Object If CacheDirect.P9 <> "" Then ' 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("データエラー " & CacheDirect.P9, MsgBoxStyle.OkOnly, "データエラー") End If End Sub
Errorイベントに関しても同様の変更を行います。
'Private Sub VisM1_OnError(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles VisM1.OnError Dim status As Object 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("Cache' Direct Error " & VisM1.ErrorName, MsgBoxStyle.OKOnly, "Cache' Direct Error") 'エラーが発生した時点でのローカル変数をダンプする CacheDirect.PrintLocalVariable() End Sub
Private Sub CacheDirect_OnError(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CacheDirect.ErrorEvent Dim status As Object ' 'UPGRADE_WARNING: オブジェクト status の既定プロパティを解決できませんでした。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6A50421D-15FE-4896-8A1B-2EC21E9037B2"' をクリックしてください。 status = MsgBox("Cache' Direct Error " & CacheDirect.ErrorName, MsgBoxStyle.OkOnly, "Cache' Direct Error") ''エラーが発生した時点でのローカル変数をダンプする 'CacheDirect.PrintLocalVariable() MsgBox("エラーが発生しました" & CacheDirect.ErrorName, MsgBoxStyle.OkOnly) End Sub
次に検索した結果で検索するためのOKボタンが押された時の処理です
Private Sub CmdOK_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdOK.Click Dim result() As String CacheDirect.Clear() CacheDirect.P0 = VB6.GetItemString(ListLookupName, ListLookupName.SelectedIndex) '選択されている名前に対応するデータを検索する。 CacheDirect.Execute(("Do GetData^ADBK(P0)")) 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then Exit Sub End If '検索フォームを非表示 Me.Hide() '取得データをフォームフィールドに展開する 'CType(ADBKMain.Controls("TxtNAME"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 1) 'CType(ADBKMain.Controls("TxtZIP"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 2) 'CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 3) 'CType(ADBKMain.Controls("TxtTELH"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 4) 'CType(ADBKMain.Controls("TxtTELO"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 5) 'CType(ADBKMain.Controls("TxtAGE"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 6) 'CType(ADBKMain.Controls("TxtDOB"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 7) End Sub
ここでもClearメソッドが呼ばれていますが、不要と判断し、コメントアウトしました。 先ほどと同様ErrorがError_Renameに強制的に変換されていました。 VB6.GetItemStringもVB6固有のメソッドになるので、書き換えが必要でした。 次にMPieceメソッドもラッパークラス固有の関数でしたが、内容を確認すると.NetのSplit関数を使うともっとシンプルに変換できることがわかりました。 先ほどと同様に先頭にADBKMain.を付加する必要があります。
Private Sub CmdOK_Click(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles CmdOK.Click Dim result() As String 'CacheDirect.Clear() 'CacheDirect.P0 = VB6.GetItemString(ListLookupName, ListLookupName.SelectedIndex) If (ListLookupName.SelectedIndex >= 0) Then ADBKMain.CacheDirect.P0 = ListLookupName.Items(ListLookupName.SelectedIndex).ToString() '選択されている名前に対応するデータを検索する。 ADBKMain.CacheDirect.Execute(("Do GetData^ADBK(P0)")) 'If CDbl(CacheDirect.Error_Renamed) <> 0 Then Exit Sub End If '検索フォームを非表示 Me.Hide() '取得データをフォームフィールドに展開する 'CType(ADBKMain.Controls("TxtNAME"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 1) 'CType(ADBKMain.Controls("TxtZIP"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 2) 'CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 3) 'CType(ADBKMain.Controls("TxtTELH"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 4) 'CType(ADBKMain.Controls("TxtTELO"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 5) 'CType(ADBKMain.Controls("TxtAGE"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 6) 'CType(ADBKMain.Controls("TxtDOB"), Object).Text = CacheDirect.MPiece(CacheDirect.P1, Chr(2), 7) result = ADBKMain.CacheDirect.P1.ToString().Split(Chr(2)) CType(ADBKMain.Controls("TxtNAME"), Object).Text = result(0) CType(ADBKMain.Controls("TxtZIP"), Object).Text = result(1) CType(ADBKMain.Controls("TxtADDRESS"), Object).Text = result(2) CType(ADBKMain.Controls("TxtTELH"), Object).Text = result(3) CType(ADBKMain.Controls("TxtTELO"), Object).Text = result(4) CType(ADBKMain.Controls("TxtAGE"), Object).Text = result(5) CType(ADBKMain.Controls("TxtDOB"), Object).Text = result(6) End If End Sub
次にロード処理ですが、このフォーム上では何もする必要がないので、VisMコントロールのロード処理をコメントアウトします。
Private Sub FindByName_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load 'CacheDirect.VisM = CType(Controls("VisM1"), Object) End Sub
フォームのクローズ処理では、エミュレータクラスの終了処理は不要なので、その処理をコメントアウトします。
Private Sub FindByName_FormClosed(ByVal eventSender As System.Object, ByVal eventArgs As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed 'UPGRADE_NOTE: オブジェクト CacheDirect をガベージ コレクトするまでこのオブジェクトを破棄することはできません。 詳細については、'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="6E35BFF6-CD74-4B09-9689-3E1A43DF8969"' をクリックしてください。 'CacheDirect = Nothing End Sub
これで修正は終了で、アプリケーションも動作するはずです。
autoinstall.basとVmclass.clsは移行の結果不必要になったので、プロジェクトから削除します。
アプリケーション実行
エミュレータクラスのロード
OpenExchangeのサイトからCacheDirect.Emulator.clsをダウンロードし、それをIRISにロード(USERネームスペース)します。
アプリケーションルーチン、グローバル、クラスのロード
ADBKサンプルのGitHubからUser/ADBK.cls,ADBK.mac,VISMUTIL.mac,adbkglb.xmlをダウンロードし、c:\tempにコピーします。
アプリケーションの実行を行うと最初に必要なグローバル、クラス、ルーチンのロードを行うはずです。
最後に
全く無修正というわけにはいきませんが、既存の資産を生かしつつ、VisM.OCXを使用したアプリケーションの移行が簡単にできるということをご理解いただき、VisMアプリケーションの移行にチャレンジしていただきたいと思います。
もう一つ、今回はサーバー側の処理は全く修正していない点も強調しておきたいと思います。
ついでに今回のサンプルでも所々実施したように古い処理をリファクタリングするのにもいい機会だと思います。 Delphiなど.Netをサポートしていない開発ツールでVisM.OCXを使っていた人のためにODBC版も作ってみました。https://github.com/wolfman0719/CacheDirectODBCEmulator
記事
Mihoko Iijima · 2023年10月10日
開発者の皆さん、こんにちは。
この記事では、複雑なJSON形式の文書を「JSONテンプレートエンジン」を利用して生成させる方法をご紹介します。
「JSONテンプレートエンジン」については、6月のウェビナーで使用例をご紹介しましたが、JSON生成対象として医療情報交換標準規格であるFHIRリソースのJSON(例:Patientリソース)を例に解説しています。
このエンジンは、JSON形式の文書であればどのような種類のデータでもご利用いただけますので、一般的なJSON形式の文書を利用して使い方をご紹介します。
例に使用するJSONはこちら👉 https://api.openbd.jp/v1/get?isbn=978-4-7808-0204-7&pretty
このサンプルから以下の部分を抜き出して、ObjectScriptでJSON形式の文書を組み立てていく方法をご紹介します。
{
"onix": {
"RecordReference": "9784780802047",
"NotificationType": "03",
"ProductIdentifier": {
"ProductIDType": "15",
"IDValue": "9784780802047"
},
"DescriptiveDetail": {
"ProductComposition": "00",
"ProductForm": "BA",
"ProductFormDetail": "B108",
"TitleDetail": {
"TitleType": "01",
"TitleElement": {
"TitleElementLevel": "01",
"TitleText": {
"collationkey": "オニギリレシピイチマルイチ",
"content": "おにぎりレシピ101"
},
"Subtitle": {
"collationkey": "エヴリディオニギリイチマルイチヘルシーイージージャパニーズライスボールレシピズ",
"content": "EVERYDAY ONIGIRI 101 Healthy, Easy Japanese Riceball Recipes"
}
}
},
"Contributor": [
{
"SequenceNumber": "1",
"ContributorRole": [
"A01"
],
"PersonName": {
"collationkey": "ヤマダ レイコ",
"content": "山田 玲子"
},
"BiographicalNote": "クッキングアドバイザー。\n東京・浜田山の自宅にて料理教室「Salon de R」を主宰。「マダムなおうちごはん」から「おもてなしのコーディネイト」を楽しく大胆に調理する方法を伝授し、笑いあふれる料理教室として人気沸騰中。\n国内での出張料理教室のみならず、食は一番身近な外交と、NYやヒューストン、シンガポール、韓国など海外でも定期的に料理教室を開催。facebookで「おにぎり外交倶楽部」を立ち上げ、おにぎりの輪を世界に広げている。\n\nReiko Yamada (Cooking adviser)\nReiko hosts “Salon de R,” a cooking workshop at her private home in Tokyo. Her 30 years of experience as a food coordinator at a children’s international summer camp inspired the idea of food as the most basic form of diplomacy. She now hosts workshops throughout Japan and internationally in Singapore, Korea, New York City, and Houston. In addition, Reiko develops recipes for companies, and contributes to magazines and radio. Most recently, she launched a Facebook page called “Onigiri Diplomacy Club” to spread the onigiri circle around the world."
},
{
"SequenceNumber": "2",
"ContributorRole": [
"A01"
],
"PersonName": {
"collationkey": "ミズノ ナオ",
"content": "水野 菜生"
}
}
]
}
}
本題に入る前に・・・
ObjectScriptではJavaScriptの構文と同様にJSONの操作が行えます。(この記事でご紹介するJSONテンプレートエンジンを使用しなくても上記文書の生成は行えます。)
例えば以下のイメージです。
💡ObjectScriptでのJSON操作について詳しくは、解説ビデオとサンプルコードが含まれている「【はじめてのInterSystems IRIS】セルフラーニングビデオ:アクセス編:IRIS での JSON の操作」をご参照ください。)
set onix={}
set onix.RecordReference="9784780802047"
set onix.NotificationType="03"
set onix.ProductIdentifier={}
set onix.ProductIdentifier.ProductIDType="15"
set onix.ProductIdentifier.IDValue="9784780802047"
set con(0)={} //Contributor用オブジェクト
set con(0).SequenceNumber="1"
set con(0).ContributorRole=["A01","A02"]
set con(0).PersonName={"collationkey":"ヤマダ レイコ","content": "山田 玲子"}
set con(0).BiographicalNote="クッキングアドバイザー。\n東京..."
set onix.Contributor=[] //Contributor配列の用意
do onix.Contributor.%Set(0,con(0)) //配列に作成したContributorオブジェクトを追加
set onix.summary={"isbn":"9784780802047","title": "おにぎりレシピ101"}
set data=[]
do data.%Set(0,onix)
set f=##class(%JSON.Formatter).%New() //JSONを見やすいフォーマットに変更するためのインスタンス生成
do f.Format(data.%ToJSON()) //JSON表示
このままコーディングしていくこともできますが、Contributor配列にはContributorオブジェクトが複数登場したり、上記例に抜粋した文書以外にも、同様に複数のJSONオブジェクトが含まれるJSON配列が多数登場します。(TextContent、SupportingResource、ImprintIdentifier、PublisherIdentifier、PublishingDate、jyuhan、author、reviews)
また、一部データが固定値の場合、その都度JSONオブジェクトや配列に固定値を記述するのは面倒ですし、すべて手動でコーディングするためにはJSON全体の構造を把握しておく必要があります。
💡そこで💡
JSONテンプレートエンジンを利用して、手間のかかりそうな操作を簡略化する手順をご紹介します。
基本の使い方については、ウェビナーアーカイブビデオ👇の 19:44 ぐらいまで、ご覧ください。
手順は以下の通りです。
1) 生成したいJSONのテンプレートとなる文書をJSON形式で用意する
2) JSONテンプレートエンジン用クラスをIRISにインポートする
3) 2)でインポートしたJSONTemplate.Baseをスーパークラスに持つクラスを新規で作成する
4) 3)のクラスで、XDataブロックのTemplateをオーバーライドし、1)で用意してテンプレートを貼り付ける
5) 3)のクラスに、動的に変わる値を操作するためのプロパティを定義し、テンプレートに割り当てる
6) JSONの生成
では早速、作成してみましょう!
1) 生成したいJSONのテンプレートとなる文書をJSON形式で用意する
https://api.openbd.jp/v1/get?isbn=978-4-7808-0204-7&pretty の例文の一部分を利用します。詳細は上述のJSONをご覧ください。
2) JSONテンプレートエンジン用クラスをIRISにインポートする
JSONテンプレートエンジンはこちら👉 https://github.com/Intersystems-jp/JSONTemplate
上記リポジトリに含まれる、以下2つのクラスをIRISにインポートしてください。
https://github.com/Intersystems-jp/JSONTemplate/tree/main/src/JSONTemplate 以下にあるJSONTemplate.Baseクラス とJSONTemplate.Generatorクラス
※スタジオや管理ポータルから *.clsのクラス定義ファイルをインポートできます。
3) 2)でインポートしたJSONTemplate.Baseをスーパークラスに持つクラスを新規で作成する
定義文は以下の通り。
Class TestEngine.SampleJSON Extends JSONTemplate.Base
4) 3)のクラスで、XDataブロックのTemplateをオーバーライドし、1)で用意してテンプレートを貼り付ける
VSCode/スタジオのオーバーライドメニューを利用して、XData ブロックのTeamplte()をオーバーライドします。
まずは、1)で考えたテンプレートのJSONを{}内に転記します。
Template()の中身:加工前
XData Template [ MimeType = application/json ]
{
{
"onix": {
"RecordReference": "9784780802047",
"NotificationType": "03",
"ProductIdentifier": {
"ProductIDType": "15",
"IDValue": "9784780802047"
},
"DescriptiveDetail": {
"ProductComposition": "00",
"ProductForm": "BA",
"ProductFormDetail": "B108",
"TitleDetail": {
"TitleType": "01",
"TitleElement": {
"TitleElementLevel": "01",
"TitleText": {
"collationkey": "オニギリレシピイチマルイチ",
"content": "おにぎりレシピ101"
},
"Subtitle": {
"collationkey": "エヴリディオニギリイチマルイチヘルシーイージージャパニーズライスボールレシピズ",
"content": "EVERYDAY ONIGIRI 101 Healthy, Easy Japanese Riceball Recipes"
}
}
},
"Contributor": [
{
"SequenceNumber": "1",
"ContributorRole": [
"A01"
],
"PersonName": {
"collationkey": "ヤマダ レイコ",
"content": "山田 玲子"
},
"BiographicalNote": "クッキングアドバイザー。\n東京・浜田山の自宅にて料理教室「Salon de R」を主宰。「マダムなおうちごはん」から「おもてなしのコーディネイト」を楽しく大胆に調理する方法を伝授し、笑いあふれる料理教室として人気沸騰中。\n国内での出張料理教室のみならず、食は一番身近な外交と、NYやヒューストン、シンガポール、韓国など海外でも定期的に料理教室を開催。facebookで「おにぎり外交倶楽部」を立ち上げ、おにぎりの輪を世界に広げている。\n\nReiko Yamada (Cooking adviser)\nReiko hosts “Salon de R,” a cooking workshop at her private home in Tokyo. Her 30 years of experience as a food coordinator at a children’s international summer camp inspired the idea of food as the most basic form of diplomacy. She now hosts workshops throughout Japan and internationally in Singapore, Korea, New York City, and Houston. In addition, Reiko develops recipes for companies, and contributes to magazines and radio. Most recently, she launched a Facebook page called “Onigiri Diplomacy Club” to spread the onigiri circle around the world."
},
{
"SequenceNumber": "2",
"ContributorRole": [
"A01"
],
"PersonName": {
"collationkey": "ミズノ ナオ",
"content": "水野 菜生"
}
}
]
}
}
}
この後、Template()に記載したJSONの中で動的に値を変更させたい部分を決定し、プロパティ値を割り当てられるように専用のプレースホルダー(#()#)を利用してプロパティ値をJSON内に当てはめるように記述を変更します。
5) 3)のクラスに、動的に変わる値を操作するためのプロパティを定義し、テンプレートに割り当てる
例えば、水色、緑、ピンク、青で囲われた部分が動的に変更するデータだとします。
上記カラーで囲った要素に動的に値を当てはめられるように、以下左図のプロパティを定義します。
また、XDataブロックのTemplate()のデータする要素に専用のプレースホルダー #()# を利用してプロパティ名を指定します(以下右図)。例: #(..プロパティ名)#
次に、Contributorの情報に👇注目👇します。
ContributorはJSON配列で、複数のJSONオブジェクトが含まれるため、下の右図のように配列に含まれるJSONオブジェクトだけを表現するテンプレートクラス(例:TestEngine.DataType.Contributor)を用意する方法も検討できます(このクラスもJSONTemplate.Baseクラスを継承しています)。
Contributor配列を表現するテンプレートクラスが作成できたら、TestEngine.SampleJSONクラスのXDataブロックを以下のように加工します。
(Contributorに複数のオブジェクトが登録できるように、コレクションのリストとして定義します)
以上でテンプレートクラスの作成は終了です。
TestEngine.SampleJSONクラスの例
Class TestEngine.SampleJSON Extends JSONTemplate.Base
{
Property TitleTextKey As %String(MAXLEN = 1000);
Property TitleTextContent As %String(MAXLEN = 1000);
Property SubtitleKey As %String(MAXLEN = 1000);
Property SubtitleContent As %String(MAXLEN = 1000);
Property Contributor As list Of TestEngine.DataType.Contributor;
XData Template [ MimeType = application/json ]
{
{
"onix": {
"RecordReference": "9784780802047",
"NotificationType": "03",
"ProductIdentifier": {
"ProductIDType": "15",
"IDValue": "9784780802047"
},
"DescriptiveDetail": {
"ProductComposition": "00",
"ProductForm": "BA",
"ProductFormDetail": "B108",
"TitleDetail": {
"TitleType": "01",
"TitleElement": {
"TitleElementLevel": "01",
"TitleText": {
"collationkey": "#(..TitleTextKey)#",
"content": "#(..TitleTextContent)#"
},
"Subtitle": {
"collationkey": "#(..SubtitleKey)#",
"content": "#(..SubtitleContent)#"
}
}
},
"Contributor": ["#(..Contributor)#"]
}
}
}
}
ClassMethod Test()
{
set obj=..%New()
set obj.TitleTextKey="オニギリレシピイチマルイチ"
set obj.TitleTextContent="おにぎりレシピ101"
set obj.SubtitleKey="エヴリディオニギリイチマルイチヘルシーイージージャパニーズライスボールレシピズ"
set obj.SubtitleContent="EVERYDAY ONIGIRI 101 Healthy, Easy Japanese Riceball Recipes"
//Contributor作成
set con=##class(TestEngine.DataType.Contributor).%New()
set con.SequenceNumber=1
set con.PersonNameKey="ヤマダ レイコ"
set con.PersonNameContent="山田 玲子"
do con.ContributorRole.Insert("A01")
do con.ContributorRole.Insert("A02")
set con.BiographicalNote="クッキングアドバイザー。\n東京・浜田山の自宅にて料理教室「Salon de R」を主宰。「マダムなおうちごはん」から「おもてなしのコーディネイト」を楽しく大胆に調理する方法を伝授し、笑いあふれる料理教室として人気沸騰中。\n国内での出張料理教室のみならず、食は一番身近な外交と、NYやヒューストン、シンガポール、韓国など海外でも定期的に料理教室を開催。facebookで「おにぎり外交倶楽部」を立ち上げ、おにぎりの輪を世界に広げている。\n\nReiko Yamada (Cooking adviser)\nReiko hosts “Salon de R,” a cooking workshop at her private home in Tokyo. Her 30 years of experience as a food coordinator at a children’s international summer camp inspired the idea of food as the most basic form of diplomacy. She now hosts workshops throughout Japan and internationally in Singapore, Korea, New York City, and Houston. In addition, Reiko develops recipes for companies, and contributes to magazines and radio. Most recently, she launched a Facebook page called “Onigiri Diplomacy Club” to spread the onigiri circle around the world."
do obj.Contributor.Insert(con)
set con=##class(TestEngine.DataType.Contributor).%New()
set con.SequenceNumber=2
set con.PersonNameKey="ミズノ ナオ"
set con.PersonNameContent="水野 菜生"
do con.ContributorRole.Insert("A01")
do obj.Contributor.Insert(con)
do obj.OutputToDevice()
}
}
TestEngine.DataType.Contributorクラスの例
Class TestEngine.DataType.Contributor Extends JSONTemplate.Base
{
Property SequenceNumber As %Integer;
Property ContributorRole As list Of %String;
Property PersonNameKey As %String;
Property PersonNameContent As %String;
Property BiographicalNote As %String(MAXLEN = 1000);
XData Template [ MimeType = application/json ]
{
{
"SequenceNumber": "#(..SequenceNumber)#",
"ContributorRole": ["#(..ContributorRole)#"],
"PersonName": {
"collationkey": "#(..PersonNameKey)#",
"content": "#(..PersonNameContent)#"
},
"BiographicalNote": "#(..BiographicalNote)#"
}
}
}
Class TestEngine.DataType.Contributor Extends JSONTemplate.Base
{
Property SequenceNumber As %Integer;
Property ContributorRole As list Of %String;
Property PersonNameKey As %String;
Property PersonNameContent As %String;
Property BiographicalNote As %String(MAXLEN = 1000);
XData Template [ MimeType = application/json ]
{
{
"SequenceNumber": "#(..SequenceNumber)#",
"ContributorRole": ["#(..ContributorRole)#"],
"PersonName": {
"collationkey": "#(..PersonNameKey)#",
"content": "#(..PersonNameContent)#"
},
"BiographicalNote": "#(..BiographicalNote)#"
}
}
}
最後に、作成したテンプレートクラスを利用してJSONを生成してみましょう!
6) JSONの生成
テンプレートクラスのインスタンスを生成し、定義したプロパティに値を割り当てます。
値が割り当て終わったら OutputToDevice()メソッドを使用してJSONの出力テストを行います(サンプルデータは https://api.openbd.jp/v1/get?isbn=978-4-7808-0204-7&pretty のデータを利用しています)。
set obj=##class(TestEngine.SampleJSON).%New()
set obj.TitleTextKey="オニギリレシピイチマルイチ"
set obj.TitleTextContent="おにぎりレシピ101"
set obj.SubtitleKey="エヴリディオニギリイチマルイチヘルシーイージージャパニーズライスボールレシピズ"
set obj.SubtitleContent="EVERYDAY ONIGIRI 101 Healthy, Easy Japanese Riceball Recipes"
//Contributor作成
set con=##class(TestEngine.DataType.Contributor).%New()
set con.SequenceNumber=1
set con.PersonNameKey="ヤマダ レイコ"
set con.PersonNameContent="山田 玲子"
do con.ContributorRole.Insert("A01")
do con.ContributorRole.Insert("A02")
set con.BiographicalNote="クッキングアドバイザー。\n東京・浜田山の自宅にて料理教室「Salon de R」を主宰。「マダムなおうちごはん」から「おもてなしのコーディネイト」を楽しく大胆に調理する方法を伝授し、笑いあふれる料理教室として人気沸騰中。\n国内での出張料理教室のみならず、食は一番身近な外交と、NYやヒューストン、シンガポール、韓国など海外でも定期的に料理教室を開催。facebookで「おにぎり外交倶楽部」を立ち上げ、おにぎりの輪を世界に広げている。\n\nReiko Yamada (Cooking adviser)\nReiko hosts “Salon de R,” a cooking workshop at her private home in Tokyo. Her 30 years of experience as a food coordinator at a children’s international summer camp inspired the idea of food as the most basic form of diplomacy. She now hosts workshops throughout Japan and internationally in Singapore, Korea, New York City, and Houston. In addition, Reiko develops recipes for companies, and contributes to magazines and radio. Most recently, she launched a Facebook page called “Onigiri Diplomacy Club” to spread the onigiri circle around the world."
//1件目のContributorをInsert()メソッドで割り当てる
do obj.Contributor.Insert(con)
set con=##class(TestEngine.DataType.Contributor).%New()
set con.SequenceNumber=2
set con.PersonNameKey="ミズノ ナオ"
set con.PersonNameContent="水野 菜生"
do con.ContributorRole.Insert("A01")
//2件目のContributorをInsert()メソッドで割り当てる
do obj.Contributor.Insert(con)
do obj.OutputToDevice()
テンプレートクラスのインスタンスからJSONを生成するメソッドですが、カレントデバイスにJSON文字列を出力するメソッドの他に、ストリームに出力するメソッドもあります。
set stream=##class(%Stream.TmpBinary).%New()
write obj.OutputToStream(stream) //成功すると1が戻ります。
//ストリームにJSON文字列が出力されたか確認
write stream.Read()
また、IRISのダイナミックオブジェクトに変換する方法もあります(引数に参照渡しでJSONのダイナミックオブジェクトを設定する引数を指定します)。
write obj.OutputToDynamicObject(.jobj)
do jobj.%ToJSON() //生成したダイナミックオブジェクトからJSON文字列をカレントデバイスに出力
ファイルに出力する方法もあります(引数に出力するファイルをフルパスで指定します)。
write obj.OutputToFile("c:\temp\test.json") //処理が成功すると1を返します。
※JSON出力メソッドについて詳しくは、アーカイブビデオの22:10~をご参照ください。
JSONの生成については、ObjectScriptでダイナミックオブジェクトを利用しながら作成していく方法でも、JSONテンプレートエンジンを利用する方法でもどちらでも作成できます。
お好みの方法をご利用ください😀 FHIRリソースを生成するにあたり、JSONTemplateを活用させていただいております!このJSONTemplateを利用している中で、1点困っていることがあります。Decimal型(DoubleやFloatでも)において、小数部が0になるときにおいて本来であれば "decimalVal" : 180.0のように出力してほしいところ "decimalVal" : 180と、小数部が省略されて出力されてしまいます。IRISにおいては、数値をキャノニック形式で表示しているためこのような現象が起きえているのかと想定しています。ただ、今回のJSONTemplateを利用した形式ではなく、%JSON.Adapter を継承したクラスにおいて%JSONExportToString を実行すると意図した値で出力できるため何かしらの解決策はあるのではないかとおもっておりますが、現状行き詰っている状態です。 watanabeさんこんにちは。ご連絡ありがとうございます。数値の場合、+をつけて数値化しておりましたが、SCALEパラメータを見て精度を合わせるようにしたいと思います。少しお待ちいただければと思います。
@Kazuma.Watanabe Watanabeさんこんにちは。JSONTemplateは%DynamicObjectを使用してまして、その制限によってダブルクオートがついてしまうのですが、数値のプロパティで指定されたSCALEパラメータの精度に合わせるようにしました。JSONTemplateリポジトリのdevブランチにアップロードしています。https://github.com/Intersystems-jp/JSONTemplate/tree/dev/src/JSONTemplateもしよろしければ、お試しいただければと思います。よろしくお願いします。 Minamoto さん早々のご確認・ご対応ありがとうございました。SCALEにより、小数部が指定された桁数になることは確認できました。ただ、やはり今回についてはダブルクォーテションが付与されてしまうことが問題となっております。FHIRの要素型上、Decimalで定義されているものになってしまうのでダブルクォーテーションがついてしまうと、バリデータ側でのエラーにかかってしまいます。やはり、%DynamicObject を使用している限りこちらの問題は難しいという認識でよろしいでしょうか? @Kazuma.Watanabe Watanabeさんこんにちは。%DynamicObjectをJSON形式で出力するのに%ToJSON()を使っていたのですが、%JSON.Formatterクラスでも出力できましたので、そのクラスを継承して、SCALEを指定しているデータのダブルクオートを出力しないようにしました。同様にDEVブランチにPushしておきましたので、ご確認いただければと思います。よろしくお願いします。 @miniclub
minamotoさん
早々のご対応ありがとうございます。OutputToDevice / OutputToStream において、想定している通りのJSONが出力できることが確認できました。OutputToDynamicObject においては処理内で$char(0)を付加している影響かNul制御文字が付与されてしまっているようです。{"intData":-123.4567,"decimal":"\u0000-123.457","decimalDataList":["\u0000-123","\u0000-123.457"],"numData":["\u0000-123.46","\u0000-123.457",-123.4567],"floatData":[-123.4567,"\u0000-123.457"],"doubleData":[-123.4567,"\u0000-123.457"]} @Kazuma.Watanabe Watanabeさんおはようございます。DynamicObjectの件、うっかりしておりました。DynamicObjectでは数値の精度を設定することはできませんので、OutputToDynamicObject()では従来のフォーマットで出力するようにしました。
記事
Toshihiko Minamoto · 2021年8月25日
この記事では、Caché Webアプリケーション(主にREST)のテストとデバッグを外部ツールを用いて行うことについて説明します。 [パート2](https://community.intersystems.com/post/debugging-web-part-2)では、Cachéツールの使用について説明します。
サーバー側のコードを作成したのでクライアントからテストしたい、またはすでにWebアプリケーションが存在するが機能していない― そういったときに使用できるのがデバッグです。 この記事では、最も使いやすいツール(ブラウザ)から最も包括的なツール(パケットアナライザー)までを説明しますが、まずは、最も一般的なエラーとその解決方法について少し説明します。
### エラー
**401 Unauthorized**
これは、本番環境へのデプロイ中に最も頻繁に発生するエラーだと思います。 ローカル開発サーバーには通常、最小限のセキュリティ設定か、バニラを除く通常セキュリティ設定が構成されています。 一方の本番サーバーには、より制限的なスキームが適用されます。 つまり、以下を確認してください。
* ログイン済みであること
* アクセスするdatabase/table/procedure/row/columnへのアクセス権があること
* 許可されていないユーザーがOPTIONSリクエストを実行できること
**404 Not Found**
以下を確認してください。
* URLが正しいこと
* 新しいアプリケーションであり、外部Webサーバーを使用している場合は、Webサーバーをリロードしてください。
**アプリケーションエラー**
最も簡単に見つけるには、スタックトレースを使用できます。 解決策は、完全にアプリケーション固有です。
### デバッグツール
**Webブラウザ**
必ず最初に利用できるデバッグツールはWebブラウザです。Chromeが推奨されますが、Firefoxでも十分にデバッグできます。 GETリクエストについては、URLをアドレスバーに入力すればテストできますが、その他すべてのリクエストには、Webアプリケーションかjsコードの記述が必要です。 一般的には以下のように行います。
* F12キーを押して、デベロッパーツールを開きます。
* [Network]タブに移動します。
* [Preserve Log]チェックボックスがオンになっていない場合は、それをオンにします。
* XHRリクエストのみを表示します。
* Webアプリケーションでバグのあるアクションを実行します。

ここから、リクエストを調べて再送信できます。 Firefoxでは、リクエストを繰り返す前にリクエストを編集することも可能です。
メリット:
* いつでも利用可能
* 使いやすい(エンドユーザーは[Network]と[Console]タブのスクリーンショットを送信できます)
* エンドユーザー環境
デメリット:
* 部分送信/破損などのレスポンスを表示しない
* 大規模なレスポンスでは速度が低下する
* 大量のレスポンスでは速度が低下する
* すべて手動で行われる
**RESTクライアント**
RESTクライアントは、Webアプリケーションのテスト向けに特別に作成されたスタンドアロンのWebアプリケーションまたはWebブラウザアドオンです。 私は[Postman](https://www.getpostman.com/)を使用していますが、似たようなものはたくさん存在します。 Postmanでのデバッグは次のようになります。

Postmanはリクエストをコレクションにグループ化して処理します。 リクエストは環境に送信可能です。 環境は変数のコレクションです。 たとえば、私のCACHE@localhost環境ホスト変数は、localhostに設定されており、userは_SYSTEMに設定されています。 リクエストが送信される際は、変数は選択した環境の値に置き換えられてリクエストが送信されます。
MDX2JSONプロジェクトのサンプルの[collection](https://github.com/intersystems-ru/Cache-MDX2JSON/blob/master/MDX2JSON.postman_collection.json)と[environment](https://github.com/intersystems-ru/Cache-MDX2JSON/blob/master/CACHE.postman_environment.json)はこちらにあります。
メリット:
* 一度作成すれば、どこででも使用できる
* リクエストの制御に優れている
* レスポンスの「Pretty」表示
デメリット:
* 連鎖リクエスト(request1に対するレスポンスがrequest2またはrequest2Bを強制できる)のデバッグは依然として手動
* 部分送信/破損などのレスポンスに失敗することがある
**HTTPデバッグプロキシ**
HTTP(S) トラフィックをログに記録するスタンドアロンアプリケーション。 ログに記録されたリクエストを変更して再送信することができます。 私は[Charles](https://www.charlesproxy.com/)と[Fiddler](http://www.telerik.com/fiddler)を使用しています。

メリット:
* 部分送信/破損などのレスポンスを処理する
* レスポンスの「Pretty」表示
* HTTPSトラフィックのサポートに優れている(パケットアナライザーより)
* キャプチャセッションを保存できる
デメリット:
* リクエストを送信するために何か(Webアプリケーション/RESTクライアント/JSコード)が必要
パケットアナライザー
ネットワークを通過するトラフィックを傍受してログに記録できるコンピュータープログラム。 データストリームがネットワークを流れる際に、スニファーが各パケットをキャプチャし、必要に応じてパケットの生データをデコードします。 これが最も包括的なオプションですが、適切に動作させるには、ある程度のスキルも必要となります。 私は[WireShark](https://www.wireshark.org/)を使用しています。 以下にインストールと使用方法を簡単に説明します。
1. ローカルパケットをキャプチャする場合は、[ループバック](https://wiki.wireshark.org/CaptureSetup/Loopback)について読み、前提条件のソフトウェア(Windows用npcap)をインストールします。
2. WireSharkをインストールします。
3. [キャプチャフィルタ](https://wiki.wireshark.org/CaptureFilters)を構成します(たとえば57772のトラフィックのみをキャプチャするファイルはport 57772とします)。
4. キャプチャを開始します。
5. [表示フィルタ](https://wiki.wireshark.org/DisplayFilters)を構成します(たとえば特定のIPへのhttpトラフィックのみを表示する場合は、ip.addr == 1.2.3.4 && httpとします)。
以下は、ポート57772(キャプチャフィルタ)のhttpトラフィック(表示フィルタ)をキャプチャした例です。
メリット:
* 部分送信/破損などのレスポンスを処理する
* 大量のトラフィックをキャプチャできる
* 何でもキャプチャできる
* キャプチャセッションを保存できる
デメリット:
* リクエストを送信するために何か(Webアプリケーション/RESTクライアント/JSコード)が必要
どれを使用するか
それは目的によって異なります。 まず、リクエストをログに記録する(デバッグプロキシ、パケットアナライザー)かリクエストを生成(ブラウザ、RESTクライアント)することを目的とすることができます。
REST Web APIを開発している場合は、RESTクライアントを使用するのが、動作をテストする最速の方法です。
ただし、RESTクライアントからのリクエストが機能してもクライアントWebアプリケーションが機能しない場合は、httpデバッグプロキシとパケットアナライザーが必要となることがあります。
クライアントがあり、サーバー側APIを開発してそれを操作する場合は、httpデバッグプロキシかパケットアナライザーが必要となります。
4種類すべてのツールを理解し、使用中のものが作業に不十分となった場合に素早く切り替えられるようにしておくことをお勧めします。
適切なツールが明確である場合もあります。
私は最近、人気のあるhttp拡張プロトコル用のサーバー側APIを開発しましたが、その際の要件は次のとおりでした。
* クライアントがすでに作成済みであり、コードを変更できない。
* クライアントごとに動作が異なる。
* httpとhttpsでの動作が異なる。
* 認証タイプごとに動作が異なる。
* クライアントごとの1秒当たりのリクエスト数は最大100件である。
* 全員がRFCを無視する。
この要件で使用できるソリューションは1つしかありません。パケットアナライザーです。
または、JS消費用のREST APIを開発しているのであれば、テストに最適なツールはRESTクライアントです。
Webアプリケーションをデバッグする場合は、Webブラウザでデバッグを開始しましょう。
パート2では、Webデバッグに関してCaché側でできること(たくさんあります)について説明します。
皆さんは、クライアント側通信をデバッグする際にどのようなアプローチを採用していますか?
記事
Mihoko Iijima · 2021年6月23日
開発者のみなさん、こんにちは!
2023/2/21追記
チュートリアルページが新しくなり「Developer Hub」に変わりました!
Interoperability(相互運用性)チュートリアルについても新しくなりました。詳しくは「InterSystems Developer Hub:クリック1回で開始できるチュートリアル(4種)のご紹介」をご参照ください。
この記事では、GettingStarted ページの無料体験環境(Sandbox)で試せるチュートリアルの中から、「Connect Your Systems」(Interoperabilityのチュートリアル)の使い方をご紹介します。
GettingStarted ページでできることについては、こちらの記事でご紹介しています。
無料体験環境(Sandbox)の開始手続きについては、こちらの記事でご紹介しています。ぜひご参照ください。
Connect Your Systems では、IRIS の Interoperability メニューを利用してどのように異なるシステム同士を接続し、処理を連携させることができるのか、ビデオによる解説とチュートリアルをご用意しています。
(1) Interoperability 概要 : ビデオ(日本語字幕切り替え可)と概要説明
(オリジナルページ👉https://gettingstarted.intersystems.com/interoperability/)
(2) Combat Fraud with Transactions and AI: ビデオのみ
(オリジナルページ👉https://gettingstarted.intersystems.com/interoperability/integration-fraud/)
このページのビデオでは、InterSystems IRIS が複数のデータ・ストリームと機械学習モデルを統合して、レストランの POS で不正取引を検出する仕組みのデモをご紹介しています。
(3) Interoperability QuickStart: ビデオ(日本語字幕切り替え可)とチュートリアル
(オリジナルページ👉https://gettingstarted.intersystems.com/interoperability/quickstart-interop-2/)
※ ビデオは (1) Interoperability 概要 のビデオと共通です。
(4) Build a Smart Ticketing System(チュートリアル)
(オリジナルページは👉https://gettingstarted.intersystems.com/interoperability/red-lights-part-2/)
以下、(1) (3) (4) について画面やチュートリアルの進め方をご紹介しています。ぜひご参照ください!
(1) Interoperability 概要
オリジナルページ 👉https://gettingstarted.intersystems.com/interoperability/ に 3 分36 秒の概要解説ビデオがあります。日本語字幕を追加する方法は、図解をご参照ください。
IRIS の Interoperability(相互運用性)メニューを利用することで、異なるシステム同士のデータ共有がかつてないほど簡単になります!
ビデオにもあるように、異なるシステム同士でデータを共有しようとすると、複数のシステムからデータを取り込む必要があったり、定義したロジックに従ってメッセージをルーティングしたり、共通プロトコルでアクセスできたり、異なるプロトコルだったり、複数の言語を使用したアクセスが必要になってくるなど、要件は様々です。
InterSystems IRIS® data platform の Interoperability メニューを利用することで以下の内容を実現できます。
メタデータやメッセージ・コンテンツに基づいてメッセージをルーティングできます。
ローコード UI を使用してビジネス・ロジックの作成や更新が行えます。
接続対象システムが使用しているフォーマットに合わせてデータを変更するようなデータ変換ロジックを作成できます。
すぐに使えるコンポーネントを使用して素早く統合処理を記述したり、独自のコードを作成してさらに柔軟性を高めることができます。
システムを流れるデータに機械学習アルゴリズムを適用することもできます。
統合されたデータを 1 つのリポジトリに保存し、保存されたデータを利用して分析を行うことができます。
トラブルシューティングや監査のためにシステムを流れるメッセージをトレースできます。
(3) Interoperability QuickStart
(オリジナルページ👉https://gettingstarted.intersystems.com/interoperability/quickstart-interop-2/)
Interoperability クイックスタートチュートリアルのテーマは以下の通りです。
信号機にカメラが設置されていて、赤信号の違反を自動的に識別できるようになった、と仮定して以下の内容を実施しようとしています。
違反を識別したデータを後で分析できるようにデータベースに保存しておきたい。
違反した車が緊急車両でない場合、違反チケットを発行するため別のアプリケーションに違反者情報のデータを送信する。
このチュートリアルの中では、自動的に識別された違反データの中から、どの情報が違反チケットを発行する対象データであるかを判断するビジネス・ルールを作成し、データのルーティングを行います。
また、判断結果を送信する接続先システムが必要としているデータのみを送付できるように、データの変換も行います。
ビジネス・ルールやデータ変換は、InterSystems IRIS の管理ポータルを使用して開発できます。開発の他にも、Interoperability メニューが提供する機能を利用したメッセージのトレースなども確認していく予定です。
テーマに合わせた一連の連携の流れや接続先情報を設定保存するための定義として「プロダクション」があります。
プロダクションは、ビジネス・サービス、ビジネス・プロセス、ビジネス・オペレーションの3種類のコンポーネントを組み合わせ定義することができます。
それぞれのコンポーネント概要は以下の通りです。
ビジネス・サービス:外部システムと接続し、外部システムからの情報を受信します(プロダクションの中で一番最初に動くコンポーネントです)。
ビジネス・プロセス:ルーティングやメッセージ変換などのビジネスロジックを定義するコンポーネントでプロダクション内の処理の調整役になります。
ビジネス・オペレーション:外部システムに処理を依頼する役割があります(プロダクション内で使用していたデータなどを渡すことができます)。
メモ:このチュートリアルの流れでは、違反情報をデータベースに登録する流れの作成は含まれていません。
① Set up
InterSystems IRIS を開始してプロダクションを参照してみよう!
(1) Sandbox を開始し、IDE を開きます。
https://gettingstarted.intersystems.com/interoperability/quickstart-interop-2/#set-up のページを開き、Sandboxを開始します。
下図のように、LOGINボタンが表示されている場合は、LOGINボタンをクリックし、ログインしてください。
LOGIN後、Sandbox の情報が表示されず「PROVISION SANDBOX」ボタンが表示されていたら「PROVISION SANDBOX」ボタンをクリックし、Sandbox を作成してください。
ユーザ登録がまだの方は、こちらの記事をご覧いただき、ユーザ登録を行ってください。
(2) 「Sandbox IDE」のリンクから IDE を開きます。また「Management Portal」のリンクから管理ポータルを開きます。
管理ポータル起動時、ログイン画面が表示されたら、ユーザ名: tech パスワード:demo を入力してください。
(3) 管理ポータルの以下メニューを開きます。
Interoperability > 一覧 > プロダクション > Demo.RedLights の行を選択し「開く」ボタンをクリックします。
(4) プロダクション構成画面で と表示されている場合は、ボタンをクリックし、プロダクションを開始します。
プロダクション開始時「このプロダクションを開始してもよろしいですか?」のポップアップメッセージが表示されるので「OK」をクリックします。
プロダクション開始中の状態確認画面が表示され「完了しました」が表示されたら「OK」ボタンを押し画面を閉じて開始処理を完了します。
② Monitor message transport
データ送受信時のメッセージをトレース画面を利用して確認します。
① SetUp でプロダクションを開始すると、プロダクションに定義している監視対象ディレクトリに用意されていたサンプルファイルが読み込まれ、処理されます。
ここでは、処理されたファイルの情報をメッセージのビジュアルトレース画面を使用して確認してみます。
1、メッセージを参照する
(1) 管理ポータルの Interoperability > 表示 > メッセージ に移動します。
表示されているメッセージから、ソースの列に「RealTimeRedLightViolation」と表示されている行の一番先頭にあるメッセージを選択します。
(2) 画面右側にある「トレース」タブを選択し「完全トレースを表示」のリンクをクリックします。
(3) 開いた画面は「ビジュアルトレース」と呼びます。
この画面では、IRIS のプロダクションに設定されているコンポーネント間でどのようなメッセージが送受信されているかを確認できます。
(4) 全メッセージはデータベースに格納され、開発時やテスト時の送受信データの確認や、運用期間中にトラブルが発生した際のメッセージの確認など、様々な場面で利用できる情報です。
(5) ビジネス・サービス:RealTimeRedLightViolationでは、外部から入力された情報を元に、メッセージが作成されました。
以下の情報を確認してみましょう。
そのメッセージは、どのビジネス・プロセスに送信されたでしょうか。
ヒント:ビジネス・サービス:RealTimeRedLightViolationの列に表示されている の送付先がどこであるか確認します。
答え:Demo.TicketBPL
ビジネス・オペレーション:To_TicketApplication へ送信されていない情報はどれでしょうか。
ヒント: をクリックし画面右側で「ボディ」タブを選択します。
をクリックし同様に、画面右側の「ボディ」タブを選択し、[1] と [3] のメッセージの違いを確認します。
答え:CARTYPE
2、他のメッセージを送信してみる
(1) Sandbox の IDE を開きます。
https://gettingstarted.intersystems.com/interoperability/quickstart-interop-2/#set-up を開き「Sandbox IDE」のリンクをクリックします。
IDE を開き左画面で shared > Samples-Integration-RedLights > data > SampleFiles を開きます。
LocalRedLightViolation1-copy.csv を Ctrl + c でコピーし、shared/Samples-Integration-RedLights/data/In に Ctrl +v でペーストします。
再度、(1) の手順を利用してメッセージ・ビューワを確認すると、読み込んだ CSV からメッセージが作成されていることを確認できます。
③ Consume CSV files using the built-in Record Mapper
管理ポータルの「レコード・マップ」は、CSV などの区切りマークで区切られたデータが含まれるファイルに対する入出力処理用コードの自動生成とプロダクションで使用するメッセージ定義を簡単に生成させることができる機能です。
チュートリアルで使用するレコードマップ定義は既に用意されていますが、サンプルの CSV ファイルを使用してCSVファイルのデータがどのように解析されるか、レコードマップ定義のサンプルファイル読み込みオプションを利用して確認します。
この他、CSV ウィザードもあり、CSV のヘッダを読み込み、対応するレコードマップを自動的に作成するメニューも用意されています。
(1) 直前に開いたメッセージ・ビューワ画面があれば、画面左上部の表示されている [Interoperability] メニューをクリックします。
次に、Interoperability > 構築 > レコード・マップ > 開くボタンクリック > Demo > RedLightViolationsRecordMap を選択します。
(2) をクリックし、テストに使用したCSV ファイル /home/project/shared/Samples-Integration-RedLights/data/SampleFiles/LocalRedLightViolation1-copy.csv を選択します(ファイルの種類で「カンマ区切りファイル(*.csv)」か「すべてのファイル(*)」を選択しすると表示されます)。
サンプルファイル選択後の画面は以下の通りです。
サンプルファイルを表示させるユーティリティを利用して、意図した通りに情報が区切れるかを確認しながらレコード・マップ定義を作成できます。
サンプルファイルが目的通りの表示になる=レコードマップ定義を利用して正しく解析できることを意味しています。
画面表示中に cross-origin エラーが発生する場合は、管理ポータルの URL を https:// から http:// に変更してお試しください(s を付けずに表示してみてください)。
Sandbox は、コンテナ間でコンテンツを共有するように設定されているため、サンプルファイルのアップロードを行う場合にこの対応が必要になることがあります。
(3) 画面上部の ホーム をクリックします。
次に、Interoperability > 構成 > プロダクション を選択します。
ビジネス・サービス:RealTimeRedLightViolation を選択し、画面右側の「設定」タブを参照し、RecordMap 定義を探します。
RecordMap 定義に先ほど参照した Demo.RedLightViolationsRecordMap が設定されていることをご確認ください。
プロダクションに含まれるコンポーネント(ビジネス・サービス/プロセス/オペレーション)は、プロダクション開始中でも設定変更が行え、変更を行ったコンポーネントのみに影響を与えることができます。
確認したレコードマップを使用しているビジネス・サービスは、予め構築済のレコードマップ用ビジネス・サービスを利用しているため、コードの記述は不要です。
区切りマーク付きファイルを処理する場合、レコード・マップ定義を利用することで、プロダクションに素早く新しい処理を追加することができます。
④ Route messages, apply business logic and transformations
信号機から受信したデータは、チケットアプリケーションに送付される予定で、さらに分析用にデータベースに保存する予定です。
しかし、受信した全データをチケットアプリケーションに送付するのではなく、赤信号を無視した車両がパトカーや消防車などの許可された車両ではない場合に限り送信します。
チュートリアルのサンプルでは、受信したデータを正しく振り分けるための適切なルーティング・ロジックが作成されています。また、データベース保存用データとして必要な情報のみを送付するように設定されています。
メモ:このチュートリアルの流れでは、抽出データをデータベースへ保存するコンポーネントの作成は含まれていません。
以下の手順で、サンプルコードを確認しましょう。
(1) プロダクション構成でビジネス・プロセス:Demo.TicketBPL を選択し、右画面の「設定」タブを選択します。
情報を提供する設定 を展開し、クラス名: Demo.TicketBPL の右側に表示されている 虫眼鏡のアイコンをクリックしビジネス・プロセス・エディタを開きます。
サンプルコードは、ローコードエディタである、ビジネス・プロセス・エディタを使用しています。
この中では、許可された車種以外の違反情報を他のアプリケーションに送信するため、call アクティビティを使用して「ビジネス・オペレーション」を呼び出しています。
メモ: や をアクティビティと呼びます。
(2) ルールアクティビティ を選択します。右画面に表示される設定画面から ボタンをクリックします。
このルール画面を使用して、メッセージの処理方法を変更することができます。
サンプルでは、CarType に含まれる文字列が、許可された車種であるかをチェックしています。例では、ambulance、police、firetruck が来た時はホワイトリストとして違反チケット用アプリケーションに送付しない流れが設定されています。
ルール画面は、特定のユーザのみが修正できるようにセキュリティを追加することもできます。
ルール画面を通して、適切なユーザが処理の流れに影響のある条件値を変更するなど、ローコードで設定することができます。
チャレンジ問題:CarType が SWAT の場合も、許可された車種となるように新しいルールを追加してください。
(3) ビジネス・プロセス:Demo.TicketBPL のビジネス・プロセス・エディタの画面に戻ります(チュートリアルの手順では、別タブに開いています)。
もし、画面を開いていない場合は、ホーム > Interoperability > 構築 > ビジネス・プロセス から開いてください。
をクリックし、アクティビティの設定画面から ボタンをクリックします(アクティビティ選択後、右画面に表示される設定画面を少しスクロールすると「リクエスト・ビルダ」ボタンが見つかります)。
データベースに保存したい情報の抽出をこの画面で行っています。
左半分に表示されている情報は、ビジネス・プロセスに送付される情報です。右半分に表示されている情報は、呼び出し対象として設定しているビジネス・オペレーションに送付する情報です。
チュートリアルテーマのように簡単なデータ変換の場合は(例では、情報の一部をカットしているだけ)、ビジネス・プロセス内で情報抽出が行えます。
より複雑なデータ変換が必要なケースもあります。その場合は、上図と同様に線を引きながら設定できるデータ変換画面を使用して、アプリケーション全体から呼び出し可能なデータ変換定義を作成することもできます。
このチュートリアルの中では、ビジネス・サービスとビジネス・オペレーションに対してコードを記述していません(予め構築済のビジネス・サービス、ビジネス・オペレーション用コードを利用しているためコードの記述は不要でした)。
もちろん、ビジネス・サービス、ビジネス・オペレーションをコードを記述しながら開発することもできます。
例えば、TCP、FTP、SQL、RESTなど接続周りの処理を簡単に記述できるアダプタを使用して、接続先のシステムから送受信する情報に合わせてカスタムコードを記述しながら開発していく方法もあります。
(4) Build a Smart Ticketing System
このチュートリアルでは、1つ前の (3) Interoperability QuickStart で確認した Red Light Violationアプリケーションをベースにしているため、説明の流れが (3) Interoperability QuickStart チュートリアルを完了した状態を想定して手順が記述されています。
このチュートリアルのテーマは
シカゴの交通システムのデータに基づいて危険な交差点を特定するための処理を追加します。
です。以下の内容を作成します。
ファイル入力によりデータを受信し、判断結果をファイル出力するシンプルなインターフェースを使用します。
赤信号違反の数に基づいてハイリスクと判断された交差点のみをリストアップするロジックを追加します。
公開されている REST API を使用して人口密度に関するデータを取得する処理を追加します。
システムに適したフォーマットにデータを修正する処理を追加します。
① Get set up
(1) Sandbox の IDE を開きます。
https://gettingstarted.intersystems.com/interoperability/red-lights-part-2/#get-set-up のページを開き、アクセス情報を確認します。
LOGIN ボタンが表示される場合は、LOGIN ボタンをクリックして下さい。「PROVISION SANDBOX」のボタンが表示される場合は「PROVISION SANDBOX」ボタンをクリックします。
(2) IDE のターミナルウィンドウを利用して、以下のチュートリアルで使用するリポジトリをクローンします。
cd /home/project/shared
git clone https://github.com/intersystems/Samples-Integration-SmartTicketing
(3) IDE のメニュー InterSystems > Management Portal から管理ポータルを開きます。
(4) サンプルコードをインポートします。
管理ポータルのメニュー システムエクスプローラ > クラス を開き、 ボタンをクリックします。
/home/project/shared/Samples-Integration-SmartTicketing/Setup/CustomCensusComponents.xml をインポートファイルに指定し、インポートを行います。
インポートの操作で cross-origin エラーが発生する場合は、管理ポータルの URL を https:// から http:// に変更し、再度実行してみてください。
Sandbox は、コンテナ間でコンテンツを共有するように設定されているため、ファイルのアップロードを成功させるにはこの方法が必要となる場合があります。
② Add a new interface
新しいインターフェースを追加してみましょう!
最初に、レコードマップを使用してファイル入力を行うシンプルなビジネス・サービスを追加します。
チュートリアルを進めていくうちに、より複雑な内容も登場しますが、現時点では、レコードマップを使用することで入力したファイルから好みのフォーマットにマッピングする処理をとても簡単に作成できることを確認します。
IRIS には、多くの構築済コンポーネントがあります。レコードマップも構築済コンポーネントを使用するため、コーディングの必要はありません。
(1) 次の手順で、シカゴの交差点に関する情報を入力するための新しいレコードマップを使用するビジネス・サービスを作成します。
A) 管理ポータルの Interoperability > 構築 > レコードマップ を開きます。
B) ボタンをクリックし、ファイルに /home/project/shared/Samples-Integration-SmartTicketing/SampleFiles/Red_Light_Camera_Violations_Test.csv を指定します。残りの設定項目は以下の通りです。
サンプルファイル:/home/project/shared/Samples-Integration-SmartTicketing/SampleFiles/Red_Light_Camera_Violations_Test.csv
レコード・マップ名:Demo.CameraViolationsMap
セパレータ:, (変更なし)
レコード終端文字:LF
文字エンコード:UTF-8 (変更なし)
3か所のチェックボックスをすべてチェック(「サンプルがヘッダ行を持つ」をチェックすることで、CSV ファイルの1行目をヘッダ行と認識します。)
設定が完了したら ボタンをクリックすると、以下の画面が開きます。
CSVファイルを指定してレコードマップを作成する操作で cross-origin エラーが発生する場合は、管理ポータルの URL を https:// から http:// に変更し、再度実行してみてください。
Sandbox は、コンテナ間でコンテンツを共有するように設定されているため、ファイルのアップロードを成功させるにはこの方法が必要となる場合があります。
C) 以下のフィールドが正しく設定されているか確認します。足りない場合は をクリックして追加してください。
D) ボタンをクリックし、レコードマップ定義と関連するメッセージクラスを生成します。
E) 管理ポータル > Interoperability > 構成 > プロダクション からプロダクション構成ページを開きます。
新しいビジネス・サービスを追加します。 赤枠のボタンをクリックします。
サービスクラスに EnsLib.RecordMap.Service.FileService を指定します(プルダウンで表示される名称には、似た名前が多いので気を付けて選んでください)。
サービス名に From_RedLightArchive を指定し、OK ボタンをクリックして追加します。
(現時点で、設定は完了していません)
プロダクションが開始していない場合は、「開始する」ボタンをクリックし、開始してください。
F) 追加したビジネス・サービスの設定を完成させます。
コンポーネント名をクリックします(From_RedLightArchive の字の部分をクリックします)。
画面右側に設定欄が表示されます。以下の項目を設定してください。
設定完了後、「適用」のボタンを押し忘れないようにご注意ください!
有効:チェックします。
ファイル・パス:/home/project/shared/Samples-Integration-RedLights/data/In/
ファイル・スペック:Red_Light_Camera_Violations*.csv
アーカイブ・パス:/home/project/shared/Samples-Integration-RedLights/data/SampleFiles/
RecordMap:Demo.CameraViolationsMap
(2) 続いて、危険な交差点のリストを出力するビジネス・オペレーションを作成します。
A) 再度、CSVウィザードを利用してレコードマップ定義を作成します。
今回のレコードマップ名は Demo.HighRiskArchiveMap とします。定義内容も前回と同じで、レコードマップ名だけ異なります。ご注意ください。
CSV ウィザードは以下メニューからも移動できます。
管理ポータル > Interoperability > 構築 > CSVレコードウィザード
上記画面の「生成」ボタンを押し忘れないようにしてください。
B) 管理ポータル > Interoperability > 構成 > プロダクション からプロダクション構成ページを開きます。
新しいビジネス・オペレーションを追加します。 赤枠のボタンをクリックします。
オペレーションクラスに EnsLib.RecordMap.Operation.FileOperationを指定します(プルダウンに表示される名称には似た名前が多いので、気を付けて選んでください)。
オペレーション名に To_HighRiskArchiveを指定し、OK ボタンをクリックして追加します。
(現時点で、設定は完了していません)
ビジネス・オペレーション:To_HighRiskArchive を選択します(コンポーネント名をクリックします)。
画面右側に設定欄が表示されます。以下の項目を設定してください。
設定完了後、「適用」のボタンを押し忘れないようにご注意ください!
有効:チェックします。
ファイル・パス:/home/project/shared/Samples-Integration-RedLights/data/Out/
ファイル名:%Q_HighRisk.txt
RecordMap:Demo.HighRiskArchiveMap
(3) ビジネス・サービス:From_RedLightArchive を選択します(コンポーネント名をクリックします)。
画面右側の設定タブの「ターゲット構成名」に作成したビジネス・オペレーション:To_HighRiskArchive を設定し、適用ボタンを押してください。
(4) プロダクションが実行中であることを確認します。
もし、停止している場合は「開始する」ボタンをクリックしてください。
(5) これでシンプルなビジネス・サービス、ビジネス・オペレーションの追加は終わりです。早速テストしてみましょう!
A) Sandbox の IDE の左画面から /home/project/shared/Samples-Integration-SmartTicketing/SampleFiles/Red_Light_Camera_Violations_Test.csv ファイルを Ctrl + c でコピーし、/home/project/shared/Samples-Integration-RedLights/data/In/ のフォルダに Ctrl + v でペーストします。
B) 管理ポータルに戻りメッセージビューワを開きます。
Menu > メッセージ・ビューワ を選択します。
C) 直近で処理されたメッセージを選択し、画面右でトレースタブを選択し「完全トレースを表示」をクリックすると、トレース画面が開きます。
ここまでの動作が正常に終了している場合、ビジネス・オペレーション:To_HighRiskArchive で設定したファイル・パスのディレクトリ(/home/project/shared/Samples-Integration-RedLights/data/Out/)にファイルが出力されている予定です。
もしエラーが発生している場合は、ビジュアルトレース画面で の表示されます。その場合は、この記事の一番下に掲載している「トラブルシューティングガイド」をご参照ください。
③ Add business orchestration logic
次に、どの交差点がハイリスクであるかを判断し、対象データのみをファイルに出力するルールセットを追加します。
ビジネス・プロセス・エディタを使い、条件のチェックやルールセットの適用などのロジックを追加し、対象となるデータを1つ前の演習で作成したビジネス・オペレーション:To_HighRiskArchive に送信し、ファイルが指定されたディレクトリに出力されるか確認します。
(1) ビジネス・プロセス・エディタを開きます。
管理ポータルの Interoperability > 構築 > ビジネス・プロセス を開きます。
(2) ボタンをクリックし、新しいビジネス・プロセスを以下の名称で作成します。
パッケージ名: Demo
名前:HighRiskIntersectionsBPL
(3) 次に、ビジネス・プロセスの設定を追加します。
ビジネス・プロセスが受信する基本要求(リクエスト・クラス)と、呼び出し元に返送する基本応答(レスポンス・クラス)、このビジネス・プロセス内で使用できるコンテキストプロパティを定義します。
画面右の「コンテキスト」タブを選択し、以下の値を設定します。
リクエスト・クラス: Demo.CameraViolationsMap.Record
レスポンス・クラス: Ens.Response(デフォルト値のまま)
コンテキストプロパティ: IntersectionRisk データタイプに %String(MAXLEN=50) を設定
コンテキストプロパティ: VIOLATIONS データタイプに %Integer を設定
設定が完了したら、 ボタンをクリックし、保存してください(アクティビティの設定が途中でも保存できます。保存時エラーメッセージが出ますが気にせず保存してください)。
(4) 次に、Assign アクティビティを追加します。
アクティビティの追加は、アクティビティのプルダウン > Assing をクリックします。
追加後、<assign> アクティビティを <start> の下に接続します。
<start> の下についているアイコン からドラッグし <assign> アクティビティの上についているアイコン までドラッグし続け、線がくっついたところでドロップします。
次に <assign> アクティビティの設定を追加します。アクティビティを選択し、右画面で以下設定してください。
アクション: set
プロパティ: context.VIOLATIONS
値: request.VIOLATIONS
メモ:ビジネス・プロセスの「コンテキスト」タブで設定したプロパティは、ビジネス・プロセス内で使用できるコンテキストオブジェクトのプロパティで 変数 context で操作できます。request はビジネス・プロセスの起動のきっかけとなったメッセージのインスタンスを操作できる変数です。
設定が完了したら、 ボタンをクリックし、保存してください(アクティビティの設定が途中でも保存できます。保存時エラーメッセージが出ますが気にせず保存してください)。
(5) <rule> アクティビティを追加します。
どの交差点がハイリスクであるかを判断する流れを、ビジネス・プロセス内で直接書くこともできますが、ルールエディタで判断基準を変更できるように、ルール(<rule> アクティビティ)を組み込みことができます。
ルールエディタを利用する利点としては、ビジネス・プロセスのコードを変更せずに、判断基準となる値のみを変更できる点です。
追加する <rule> アクティビティでは、1日の交差点通過時の違反回数から very high、hight、low のいずれかの文字列が設定される予定です。
以下の手順で追加します。
A) <rule> アクティビティを追加します。
B) キャプションを設定します。
C) <rule> アクティビティから呼び出されるルールを作成します。
<rule> アクティビティを選択し ボタンをクリックし、出力される子画面で以下入力します。
パッケージ名: Demo
名前: IntersectionRiskRule
コンテキスト・クラス: Demo.HighRiskIntersectionsBPL.Context
設定した「コンテキスト・クラス」の指定により、ビジネス・プロセスで使用してる特定の交差点の違反数を登録するコンテキスト・プロパティの値をルールに渡すことができます。
D) ルール・エディタに rule を追加します。
を選択し、 をクリックしてから を選択します。
E) 追加した rule に when を追加します。
を選択し、 をクリックしてから を選択します。
D) 1つの目の の をダブルクリックし、VIOLATIONS > 15 を条件式として設定します。
E) 1つ目の を選択し、 をクリックしてから を選択し、“very high” を value に設定します。
H) 同様の手順で、以下の図と同じ設定になるように定義してください。
I) 最後に、保存ボタンをクリックし、設定を保存します。
(6) ルールエディタは、新しいタブで開いています。
一旦ルールエディタのタブを閉じ、ビジネス・プロセス・エディタに戻ります。
ビジネス・プロセス・エディタの <rule> アクティビティの設定で、ルール名に Demo.IntersectionRiskRule を設定し、Result Location に context.IntersectionRisk を設定します。
設定が完了したら、 ボタンをクリックし、保存してください(アクティビティの設定が途中でも保存できます。保存時エラーメッセージが出ますが気にせず保存してください)。
(7) <rule> アクティビティを選択した状態で、<if> アクティビティを追加します。
メモ:接続したいアクティビティを選択した状態で新しいアクティビティを追加すると、予め線が結ばれた状態で追加されます。
<if> アクティビティのブロックの終わりを示す と <if> アクティビティがつながった状態で追加されます。
(8) <if> アクティビティを設定します。
名前: ハイリスクかどう
条件: (context.IntersectionRisk = "high") || (context.IntersectionRisk = "very high")
(IntersectionRisk の値が "high" か "very high" の場合に、その情報をファイル出力するようにこの後の問題で作成します)
(9) ハイリスクの交差点だと特定された場合、その情報をファイル出力します。
<if> アクティビティが真である場合の true の線を選択した状態で <call> アクティビティを追加します。
<call> アクティビティの設定は以下の通りです。
名前: HighRiskArchiveの呼び出し
ターゲット: オペレーションを選択し、To_HighRiskArchive を設定
リクエストメッセージクラス: Demo.HighRiskArchiveMap.Record
レスポンスメッセージクラス: Ens.Response (デフォルト設定のまま)
(10) <call> アクティビティの ボタンをクリックします。
(11) ビジネス・プロセスの起動のきっかけとなった要求メッセージ(基本要求)の request の %Source プロパティ を <call> アクティビティで設定したターゲット構成へ送付する要求メッセージの callrequest の %Source プロパティにコピーします(線を結びます)。
(線を引くとき、色が変わるまでドロップしないようにしてください)
同様に、request.INTERSECTION から context.INTERSECTION へ線を引きます。
(12) 線を引いた後、OK ボタンをクリックします。
要求メッセージ・クラスの要求アクションに と設定されます。
(13) <if> アクティビティの設定を変更します。
ブロックの終わりを示す に false の線をつなぎます。
(14) 最後に、<if> アクティビティのブロックの終わりを示すアイコンから、<end> まで線を引き完成です。
(15) 忘れずに、コンパイルを実行してください。
コンパイル結果が子画面に表示されます。「コンパイルが正常に終了ました」と表示されるまでお待ちください。
(16) 作成したビジネス・プロセスをプロダクションに追加します。
管理ポータル > interoperability > 構成 > プロダクション を開きます。
(17) ビジネス・プロセスを追加します。 をクリックします。
(18) ビジネス・プロセス・クラスに作成した Demo.HighRiskIntersectionsBPL を設定し、有効にするのチェックをいれ、OKボタンをクリックします。
(19) ビジネス・サービス:From_RedLightArchive のターゲット構成名を変更します。
Demo.HighRiskInteractionsBPL に変更し適用ボタンをクリックします。
(20) テストを実行します。
先ほどのテストと同じファイル(/home/project/shared/Samples-Integration-SmartTicketing/SampleFiles/Red_Light_Camera_Violations_Test.csv)をCtrl + c でコピーし、/home/project/shared/Samples-Integration-RedLights/data/In に Ctrl + v でペーストしてテストを実行してください。
メッセージビューワを利用して、いくつかのメッセージがビジネス・プロセスで止まっていることを確認してください。
管理ポータル > Interoperability > 表示 > メッセージ
ビジネス・プロセスで停止しているトレース例は以下の通りです。
をクリックすると、画面右側にルールのどの条件を通過したか、確認できる画面が表示されます。
のリンクをクリックすると、ルールエディタが開き、通過した場所を表示します。
以下のトレース例は、オペレーションまでメッセージが流れいてる例です(high のパタン)。
④ Add routing
先ほどまでの流れでは、危険な交差点であることが判明した場合、その情報をファイル出力していました。
ここに、各交差点の人口密度に関する情報を取得し、危険な交差点情報の追加情報としてファイル出力に加えることで、危険な交差点に対してどのような対応がとれるかより良い方法を探ることができるようになります。
作成中のビジネス・プロセス:Demo.HighRiskIntersectionsBPL に、REST を使用して US Census が提供するサービスの呼び出しを追加し、交差点の緯度と経度から GeoJSON コードを取得するように変更します。
次に、人口情報を取得するため米国国勢調査が提供する別のサービスの呼び出しを行い、取得した人口密度情報をハイリスクの交差点情報に追加し、ファイル出力を行います。
(1) プロダクション構成ページにビジネス・オペレーションを追加します。
チュートリアルのサンプルとして用意のある Demo.ToCensusGeoServiceBO クラスを使用して以下の設定で新しいビジネス・オペレーションを追加します。
オペレーションクラス: Demo.ToCensusGeoServiceBO
有効にする: チェックします
(設定タブ)HTTPサーバ: tigerweb.geo.census.gov
(設定タブ)URL: /arcgis/rest/services/TIGERweb/Tracts_Blocks/MapServer/5/
(設定タブ)SSL 設定: RESTSSL
SSL 設定は作成していないため、以下の手順で追加します。
管理ポータル > システム管理 > セキュリティ > SSL/TLS 構成 > 新規構成の作成ボタンクリック > 構成名に RESTSSL を設定し保存ボタンクリック
(2) プロダクションの設定を変更します。
管理ポータル > Interoperability > (USERネームスペースを選択)> 構成 > プロダクション を開き画面右側の「設定」タブの下の方に配置されている [開発とデバッグ]を展開し「テスト使用可能」をチェックし適用ボタンクリックします。
(3) ビジネス・オペレーション:Demo.ToCensusGeoServiceBO をテストします。
をクリック(コンポーネント名をクリック)し、画面右側の「アクション」タブを選択し ボタンをクリックします。
テスト画面が表示されるので以下入力し をクリックします。
Latitude: 41.95402905
Longtitude: -87.70807749
(4) もう1つ、ビジネス・オペレーションを追加します。
クラス名: Demo.ToCensusDS
有効にする: チェックします。
(設定タブ)HTTPサーバ: api.census.gov
(設定タブ)URL: /data/2014/acs/acs5
(設定タブ)SSL設定: RESTSSL
(5) ビジネス・プロセス: HighRiskIntersectionsBPL をビジネス・プロセス・エディタで開きます。
プロダクション構成画面で を選択し(コンポーネント名を選択し)、画面右側の設定タブの「情報を提供する設定」を展開し、クラス名の右側に表示されている虫眼鏡アイコン をクリックします。
(6) 以下の修正を行います。
Geo サービス(ビジネス・オペレーション:Demo.ToCensusGeoServiceBO)から取得した GeoJSON 情報を Census サービス(ビジネス・オペレーション:Demo.ToCensusDS)に渡すために、追加でコンテキスト・プロパテを設定します。
[コンテキスト] タブに移動し、Demo.GeoResponse タイプの GeoResponse プロパティと、%String(MAXLEN=50) タイプの Population プロパティを追加します。
(7) すべての交差点情報に緯度と経度のデータがあるとは限らないためファイル出力情報に追加できない場合もあります。
経度と緯度の情報がある場合、ない場合で処理を分けられるように新しい <if> アクティビティを追加します。
追加場所は、現在の <if> アクティビティと HighRiskArchiveオペレーション を呼び出す<call> アクティビティの間に追加します。
true の線を選択し、<if>アクティビティを追加します。
<if> アクティビティの設定内容は以下の通りです。
名前: Geo情報の有無
条件: $length(request.LATITUDE)>0
メモ:$length() は InterSystems ObjectScript の関数で、引数に指定した文字数を返します。
(8) (7)で追加した<if> アクティビティ(Geo情報の有無)で、経度と緯度の情報が取得できた場合、2 つの <call> アクティビティを追加します。
1 つ目の <call> アクティビティの設定は以下の通りです。
名前: Geo情報取得
ターゲット: Demo.ToCensusGeoServiceBO
非同期: チェックを外します(同期処理に変更します)
要求メッセージクラス: Demo.GeoRequest
要求アクション:
応答メッセージクラス: Demo.GeoResponse
応答アクション:
要求メッセージクラスの設定では、永続クラス > Demo > GeoRequest の順に選択します。
応答メッセージクラスの設定では、永続 クラス> Demo > GeoResponse の順に選択します。
要求アクションの設定は、 ボタンをクリックしても設定できます。
応答アクションの設定は、 ボタンからも設定できます。
2つ目の <call> アクティビティの設定は以下の通りです。
名前: 人口密度情報取得
ターゲット: Demo.ToCensusDS
非同期: チェックを外します(同期処理に変更します)
要求メッセージクラス: Demo.PopulationRequest
要求アクション:
応答メッセージクラス: Demo.PopulationResponse
応答アクション:
要求メッセージクラスの設定では、永続クラス > Demo > PopulationRequest の順に選択します。
応答メッセージクラスの設定では、永続クラス > Demo > PopulationResponse の順に選択します。
要求アクションの設定は、 ボタンをクリックしても設定できます。
State プロパティと同様に、Country、Tract、BackGroup も設定した例は以下の通りです。
応答アクションの設定は、 ボタンからも設定できます。
(9) Geo情報(経度と緯度の情報)がない場合、人口密度情報を取得できないため、人口密度情報を含まないハイリスク用のファイルを作成する必要があります。
Geo情報有無 の <if> アクティビティで false の場合は何も処理せず、ブロックの終わりに移動するように線を引きます。
(10) ビジネス・プロセスをコンパイルします。
(11) ハイリスクの交差点の情報を出力するファイルに人口密度情報を追加するため、レコードマップ:Demo.HighRiskArchiveMap を修正します。
管理ポータルの ホーム > Interoperability > 構築 > レコード・マップ > 開く > Demo.HighRiskArchiveMap で定義を開きます。
(レコードマップの画面は直近で操作した定義が表示されます。手順通りに進めていると Demo.HighRiskArchiveMap が開いた状態で画面が開きます。)
PopulationDensity をカラムを追加し、データタイプを %String に設定します。
をクリックし新しいカラムを追加します。
修正が完了したら、忘れずに ボタンをクリックします。ボタンを押した後の画面は以下ご参照ください。
(12) ビジネス・プロセス:Demo.HighRiskIntersectionsBPL のエディタに戻ります。
を選択し、を利用して値の割り当てを追加します。
context.Population を callrequest.PopulationDensity に割り当てを追加します(callrequest.PopulationDensity はレコードマップで追加したカラム名です)。
修正が完了したらコンパイルを行います。
(13) テストを行います。
Sandbox の IDE の左画面から /home/project/shared/Samples-Integration-SmartTicketing/SampleFiles/Red_Light_Camera_Violations_Test.csv ファイルを Ctrl + c でコピーし、/home/project/shared/Samples-Integration-RedLights/data/In/ のフォルダに Ctrl + v でペーストします。
メッセージビューワで確認します(管理ポータルの ホーム > Interoperability > 表示 > メッセージ)。
画面右側の「トレース」タブを参照しながら、追加した REST サービスを呼び出すオペレーションへの経路があるトレースを開き、流れを確認してください。
⑤ Explore custom business components
InterSystems IRIS には、コーディングをほとんど必要としないすぐに使えるコンポーネントが多く用意されています。
構築済コンポーネント以外では、InterSystems ObjectScript で作成するカスタム・コンポーネントもあり、カスタム・コンポーネントの利点としては、処理に必要なことなら何でもコードで追記することができます。
チュートリアルに登場したビジネス・オペレーションの Demo.ToCensusGeoServiceBO と Demo.ToCensusDS は、カスタム・コンポーネントとして作成しています。
以降の紹介では、InterSystems ObjectScript で記述されたカスタムコンポーネントのコードについてご説明します。
Sandbox の IDE を開きます(https://gettingstarted.intersystems.com/interoperability/red-lights-part-2/#get-set-up からアクセス情報をご確認ください)。
左画面で、shared/Samples-Integration-SmartTicketing/Setup/src/cls に移動し、Demo.ToCensusDS クラスを開きます。
このクラスの中のコードでは、REST エンドポイントを呼び出し、情報を取得して、呼び出したビジネス・プロセスにデータを返送している処理を記述しています。
以下、コードで登場する変数やメソッドについての補足です。
変数 tURL は、プロダクション構成の で設定した情報を ..Adapter.URL で取得し、ここに REST サービスを呼び出す時に必要となるクエリパラメータを追記しています。
GetURL()メソッドは、 と組み立てた URL(=変数 tURL)を利用して、GET 要求を実行し、HTTP 応答を受信しています。HTTP 応答は、参照渡しで指定された第2引数 tHttpResponse に登録されます。
..JSONStreamToObject() メソッドは、ビジネス・オペレーションが使用している REST 用のアウトバウンドアダプタ(ビジネス・オペレーションに追加できるアダプタ)から提供されるメソッドで、GET 要求から得られた HTTP 応答のストリームに含まれる JSON データを IRIS のダイナミックオブジェクト(=IRIS 内で JSON を操作するときに使用するインスタンス)に変換しています。
Demo.GeoResponse のインスタンスを新規生成(set pResponse = ##class(Demo.GeoResponse).%New())している流れでは、HTTP 応答から変換した JSON のダイナミックオブジェクトを使用して、返送データを応答メッセージのプロパティに設定しています。インスタンスを設定している変数 pResponse は、メソッド:GetGeoInfo() の第2引数に参照渡しとして設定されている引数で、この変数に情報を設定することで、ビジネス・オペレーションの応答メッセージとして呼び出し元に返送されます。
/// Retrieves GeoJSON data from the USCensus using longitude and latitude.
Method GetGeoInfo(pRequest As Demo.GeoRequest, Output pResponse As Demo.GeoResponse) As %Status
{
try {
// Prepare and log the call
// Append the city to the URL configured for adapter
set tURL= ..Adapter.URL _ "query?geometry=" _ pRequest.Longitude _ ","
_ pRequest.Latitude _ "&geometryType=esriGeometryPoint&outfields=*"
_ "&returnGeometry=true&returnIdsOnly=false&inSR=4326&f=geojson"
// Execute the call
$$$TRACE("Executing call")
set tSC=..Adapter.GetURL(tURL,.tHttpResponse)
// Return the response
if $$$ISERR(tSC)&&$IsObject(tHttpResponse)&&$IsObject(tHttpResponse.Data)
&&tHttpResponse.Data.Size {
set tSC=
$$$ERROR($$$EnsErrGeneral,$$$StatusDisplayString(tSC)_":"_tHttpResponse.Data.Read())
}
quit:$$$ISERR(tSC)
if $IsObject(tHttpResponse) {
// Instantiate the response object
set pResponse = ##class(Demo.GeoResponse).%New()
// Convert JSON into a Proxy Object
set tSC = ..JSONStreamToObject(tHttpResponse.Data, .tProxy)
if (tSC){
// Set response properties from the Proxy Object
set pResponse.State = tProxy.features.GetAt(1).properties.STATE
set pResponse.County = tProxy.features.GetAt(1).properties.COUNTY
set pResponse.Tract = tProxy.features.GetAt(1).properties.TRACT
set pResponse.BlockGroup = tProxy.features.GetAt(1).properties.BLKGRP
$$$TRACE("Object created with " _ pResponse.State)
}
}
}catch{
set tSC=$$$SystemError
}
quit tSC
}
Troubleshooting Guide
問題
対策
Sandbox が起動しない、ページが表示されない
この記事のコメント欄でお知らせください
メッセージビューワにメッセージが表示されない
ビジネス・サービス:From_RedLightArchiveの設定タブにある「ターゲット構成名」が正しく設定されているかご確認ください。
チュートリアル前半では、ターゲット構成名には To_HighRiskArchive が設定されています。
チュートリアル公判では、Demo.HighRiskIntersectionsBPL が設定されています。
cross-originエラーが発生する
管理ポータルの URL を https:// から http:// に変更し、再度実行してみてください。
Sandbox は、コンテナ間でコンテンツを共有するように設定されているため、共有フォルダ内のファイルに正常にアクセスするために必要になる場合があります。
ビジネス・オペレーション:Demo.ToCensusGeoServiceBO テスト時にエラーが発生
テスト時に入力したデータ(longitude、latitude)をご確認ください。(例に記載の文字列をご利用ください)
テストボタンが無効になっていて押せない
プロダクションの設定を確認します。設定タブの下の方にある「開発とデバッグ」を展開し、テスト使用可能のチェックを入れ 適用ボタンをクリックしてから再度お試しください。
メッセージビューワに表示されるメッセージ一覧に「Ens.ScheduleService」が表示されていて、チュートリアルの流れには登場していません。
このメッセージはバックグラウンド処理に関連したメッセージで無視いただけます。
メッセージ一覧にチュートリアルで使用しているメッセージが表示されていれば大丈夫です。
記事
Toshihiko Minamoto · 2020年11月26日
[前回](https://community.intersystems.com/post/deploying-intersystems-iris-solution-gcp-kubernetes-cluster-gke-using-circleci)は GKE サービスを使用して IRIS アプリケーションを Google Cloud 上で起動しました。
また、クラスターを手動で(または [gcloud](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-cluster) を介して)作成するのは簡単ですが、最新の [Infrastructure-as-Code(IaC)手法](https://martinfowler.com/bliki/InfrastructureAsCode.html)では、Kubernetesクラスターの説明もコードとしてリポジトリに格納する必要があります。 このコードの記述方法は、IaC に使用されるツールによって決まります。
Google Cloud の場合は複数のオプションが存在し、その中には Deployment Manager と Terraform があります。 どちらが優れているかにつては意見が分かれています。詳細を知りたい場合は、この Reddit のスレッド「Opinions on Terraform vs. Deployment Manager?」と Medium の記事「Comparing GCP Deployment Manager and Terraform」を参照してください。
この記事では特定のベンダーとの結びつきが少なく、さまざまなクラウドプロバイダーで IaC を使用できる Terraform を選択します。
ここでは過去の記事を読み、Googleアカウントを作成し、前回の記事と同様に「開発」という名前のプロジェクトを作成しているものと仮定します。 この記事ではその ID は <PROJECT_ID> として表示されます。 以下の例では、それを自分のプロジェクトの IDに変更してください。
Google には無料枠がありますが、無料ではないことに注意してください。 必ず出費をコントロールするようにしてください。
また、ここではすでに元のリポジトリをフォークしていることを前提にしています。 この記事全体を通してこのフォークを「my-objectscript-rest-docker-template」と呼び、そのルートディレクトリを「<root_repo_dir>」として参照します。
コピーと貼り付けを簡単にするため、すべてのコードサンプルをこのリポジトリに格納しています。
次の図では、デプロイプロセス全体を 1 つの図で表しています。
では、次のように執筆時点での Terraform の最新バージョンをインストールしましょう。
$ terraform versionTerraform v0.12.17
インターネット上の多くの例では旧バージョンが使用されており、0.12 では多くの変更が加えられているため、ここではバージョンが重要になります。
ここでは GCP アカウントで Terraform に特定のアクションを実行(特定の API を使用)させたいと考えています。 これを可能にするには「terraform」という名前のサービスアカウントを作成し、Kubernetes Engine API を有効にしてください。 その実施方法についてはご心配なく。この記事を読み進めるだけで、あなたの疑問は解消します。
Web Console を使用することもできますが、ここでは gcloud ユーティリティを使った例を試してみましょう。
次の例では、数種類のコマンドを使用します。 これらのコマンドや機能の詳細については、次のドキュメントのトピックを参照指定してください。
gcloud iam service-accounts create
特定のリソースのサービスアカウントへの役割の付与
gcloud iam service-accounts keys create
Google Cloud プロジェクトでの API の有効化
それでは、例を見ていきましょう。
$ gcloud init
前回の記事で gcloud を取り上げましたので、ここではセットアップの詳細は説明しません。 この例では、次のコマンドを実行します。
$ cd <root_repo_dir>$ mkdir terraform; cd terraform$ gcloud iam service-accounts create terraform --description "Terraform" --display-name "terraform"
次に、「Kubernetes Engine Admin」(container.admin)の他にいくつかのロールを terraform サービスアカウントに追加しましょう。 これらのロールは今後役に立つことでしょう。
$ gcloud projects add-iam-policy-binding <PROJECT_ID> \ --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \ --role roles/container.admin
$ gcloud projects add-iam-policy-binding <PROJECT_ID> \ --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \ --role roles/iam.serviceAccountUser
$ gcloud projects add-iam-policy-binding <PROJECT_ID> \ --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \ --role roles/compute.viewer
$ gcloud projects add-iam-policy-binding <PROJECT_ID> \ --member serviceAccount:terraform@<PROJECT_ID>.iam.gserviceaccount.com \ --role roles/storage.admin
$ gcloud iam service-accounts keys create account.json \--iam-account terraform@<PROJECT_ID>.iam.gserviceaccount.com
最後の入力では、account.json ファイルを作成していることに注意してください。 このファイルは必ず秘密にしてください。
$ gcloud projects list$ gcloud config set project <PROJECT_ID>$ gcloud services list --available | grep 'Kubernetes Engine'$ gcloud services enable container.googleapis.com$ gcloud services list --enabled | grep 'Kubernetes Engine'container.googleapis.com Kubernetes Engine API
次に、Terraform の HCL 言語で GKE クラスターを記述しましょう。 ここではいくつかのプレースホルダーを使用していますが、これらは実際の値に置き換えてください。
プレースホルダー
意味
例
<PROJECT_ID>
GCP のプロジェクト ID
possible-symbol-254507
<BUCKET_NAME>
Terraform のステート/ロック用のストレージ(一意である必要があります)
circleci-gke-terraform-demo
<REGION>
リソースが作成されるリージョン
europe-west1
<LOCATION>
リソースが作成されるゾーン
europe-west1-b
<CLUSTER_NAME>
GKE クラスター名
dev-cluster
<NODES_POOL_NAME>
GKE ワーカーノードのプール名
dev-cluster-node-pool
以下に実際のクラスターの HCL 構成を示します。
$ cat main.tfterraform { required_version = "~> 0.12" backend "gcs" { bucket = "<BUCKET_NAME>" prefix = "terraform/state" credentials = "account.json" }}
provider "google" { credentials = file("account.json") project = "<PROJECT_ID>" region = "<REGION>"}
resource "google_container_cluster" "gke-cluster" { name = "<CLUSTER_NAME>" location = "<LOCATION>" remove_default_node_pool = true # In regional cluster (location is region, not zone) # this is a number of nodes per zone initial_node_count = 1}
resource "google_container_node_pool" "preemptible_node_pool" { name = "<NODES_POOL_NAME>" location = "<LOCATION>" cluster = google_container_cluster.gke-cluster.name # In regional cluster (location is region, not zone) # this is a number of nodes per zone node_count = 1
node_config { preemptible = true machine_type = "n1-standard-1" oauth_scopes = [ "storage-ro", "logging-write", "monitoring" ] }}
HCL コードを適切に整形できるよう、Terraform には次の便利な整形コマンドが用意されています。
$ terraform fmt
上記のコードスニペットは、作成されたリソースが Google によって提供され、リソース自体は google_container_cluster と google_container_node_pool であることを示しています。また、ここではコスト削減のために preemptible を指定しています。 また、デフォルトの代わりに独自のプールを作成しています。
次の設定を簡単に説明します。
terraform { required_version = "~> 0.12" backend "gcs" { Bucket = "<BUCKET_NAME>" Prefix = "terraform/state" credentials = "account.json" }}
Terraform はすべての実行結果をステータスファイルに書き込み、このファイルを他の作業に使用します。 このファイルは共有しやすいように、離れた場所に保存することをお勧めします。 一般的には Google バケットに保存されます。
このバケットを作成しましょう。 プレースホルダー <BUCKET_NAME> の代わりに自分のバケットの名前を使用してください。 バケットを作成する前に、<BUCKET_NAME> が使用できるかどうかを次のコマンドで確認してください。すべての GCP で一意である必要があるためです。
$ gsutil acl get gs://<BUCKET_NAME>
期待する応答:
BucketNotFoundException: 404 gs://<BUCKET_NAME> bucket does not exist
「Busy」という応答があった場合、別の名前を選択する必要があります。
AccessDeniedException: 403 <YOUR_ACCOUNT> does not have storage.buckets.get access to <BUCKET_NAME>
Terraform の推奨どおりにバージョン管理も有効にしましょう。
$ gsutil mb -l EU gs://<BUCKET_NAME>
$ gsutil versioning get gs://<BUCKET_NAME>gs://<BUCKET_NAME>: Suspended
$ gsutil versioning set on gs://<BUCKET_NAME>
$ gsutil versioning get gs://<BUCKET_NAME>gs://<BUCKET_NAME>: Enabled
Terraform はモジュール方式であり、GCP で何かを作成するにはGoogle Provider プラグインを追加する必要があります。 これを行うには、次のコマンドを使用します。
$ terraform init
Terraform が GKE クラスターを作成する際の実行計画を見てみましょう。
$ terraform plan -out dev-cluster.plan
コマンドの出力には、計画の詳細が含まれています。 特に問題なければ、次のコマンドでこの計画を実行しましょう。
$ terraform apply dev-cluster.plan
ちなみに、Terraform によって作成されたリソースを削除するには、このコマンドを <root_repo_dir>/terraform/ ディレクトリから実行してください。
$ terraform destroy -auto-approve
しばらくクラスターから離れて先に進みましょう。 ただし、何もかもリポジトリにプッシュされないように、先にいくつかのファイルを例外に追加しましょう。
$ cat <root_repo_dir>/.gitignore.DS_Storeterraform/.terraform/terraform/*.planterraform/*.json
Helm の使用
前回の記事では、Kubernetes のマニフェストを yaml ファイルとして <root_repo_dir>/k8s/ ディレクトリに保存し、それを「kubectl apply」コマンドを使用してクラスターに送信しました。
今回は別の手法を試してみましょう。最近バージョン 3にアップデートされた Kubernetes のパッケージマネージャーである Helm を使用します。 バージョン 2 には Kubernetes 側のセキュリティの問題があったため、バージョン 3 以降を使用してください(詳細については、Running Helm in production: Security best practices を参照してください)。 まず、Kubernetes のマニフェストを k8s/ ディレクトリからチャートとして知られる Helm パッケージにまとめます。 Kubernetes にインストールされている Helm チャートはリリースと呼ばれます。 最小構成では、チャートは次のような複数のファイルで構成されます。
$ mkdir <root_repo_dir>/helm; cd <root_repo_dir>/helm$ tree <root_repo_dir>/helm/helm/├── Chart.yaml├── templates│ ├── deployment.yaml│ ├── _helpers.tpl│ └── service.yaml└── values.yaml
これらのファイルの目的は、公式サイトで詳細に説明されています。 独自チャートを作成するためのベストプラクティスは、Helm ドキュメントの The Chart Best Practices Guide に記載されています。
次にファイルの内容を示します。
$ cat Chart.yamlapiVersion: v2name: iris-restversion: 0.1.0appVersion: 1.0.3description: Helm for ObjectScript-REST-Docker-template applicationsources:- https://github.com/intersystems-community/objectscript-rest-docker-template- https://github.com/intersystems-community/gke-terraform-circleci-objectscript-rest-docker-template
$ cat templates/deployment.yamlapiVersion: apps/v1kind: Deploymentmetadata: name: {{ template "iris-rest.name" . }} labels: app: {{ template "iris-rest.name" . }} chart: {{ template "iris-rest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }}spec: replicas: {{ .Values.replicaCount }} strategy: {{- .Values.strategy | nindent 4 }} selector: matchLabels: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} template: metadata: labels: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} spec: containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} name: {{ template "iris-rest.name" . }} ports: - containerPort: {{ .Values.webPort.value }} name: {{ .Values.webPort.name }}
$ cat templates/service.yaml{{- if .Values.service.enabled }}apiVersion: v1kind: Servicemetadata: name: {{ .Values.service.name }} labels: app: {{ template "iris-rest.name" . }} chart: {{ template "iris-rest.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }}spec: selector: app: {{ template "iris-rest.name" . }} release: {{ .Release.Name }} ports: {{- range $key, $value := .Values.service.ports }} - name: {{ $key }}{{ toYaml $value | indent 6 }} {{- end }} type: {{ .Values.service.type }} {{- if ne .Values.service.loadBalancerIP "" }} loadBalancerIP: {{ .Values.service.loadBalancerIP }} {{- end }}{{- end }}
$ cat templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}{{/*Expand the name of the chart.*/}}
{{- define "iris-rest.name" -}}{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}{{- end -}}
{{/*Create chart name and version as used by the chart label.*/}}{{- define "iris-rest.chart" -}}{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}{{- end -}}
$ cat values.yamlnamespaceOverride: iris-rest
replicaCount: 1
strategy: | type: Recreate
image: repository: eu.gcr.io/iris-rest tag: v1
webPort: name: web value: 52773
service: enabled: true name: iris-rest type: LoadBalancer loadBalancerIP: "" ports: web: port: 52773 targetPort: 52773 protocol: TCP
Helm チャートを作成するには、Helm クライアントと kubectl コマンドラインユーティリティをインストールします。
$ helm versionversion.BuildInfo{Version:"v3.0.1", GitCommit:"7c22ef9ce89e0ebeb7125ba2ebf7d421f3e82ffa", GitTreeState:"clean", GoVersion:"go1.13.4"}
iris というネームスペースを作成します。 デプロイ中にこれが作成されていれば良かったのですが、現時点ではその動作は実装されていません。
まず、Terraform によって作成されたクラスターの資格情報を kube-config に追加します。
$ gcloud container clusters get-credentials <CLUSTER_NAME> --zone <LOCATION> --project <PROJECT_ID>$ kubectl create ns iris
Helm が Kubernetes で以下を作成することを(実際にデプロイを開始せずに)確認します。
$ cd <root_repo_dir>/helm$ helm upgrade iris-rest \ --install \ . \ --namespace iris \ --debug \ --dry-run
ここでは、出力(Kubernetes のマニフェスト)をスペースを確保するために省略しています。 特に問題がなければ、デプロイしましょう。
$ helm upgrade iris-rest --install . --namespace iris$ helm list -n iris --allIris-rest iris 1 2019-12-14 15:24:19.292227564 +0200 EET deployed iris-rest-0.1.0 1.0.3
Helm がアプリケーションをデプロイしたことはわかりますが、Docker イメージ eu.gcr.io/iris-rest:v1 をまだ作成していないため、Kubernetes がそのイメージをプルすることはできません(ImagePullBackOff)。
$ kubectl -n iris get poNAME READY STATUS RESTARTS AGEiris-rest-59b748c577-6cnrt 0/1 ImagePullBackOff 0 10m
とりあえず、今はここで終わっておきましょう。
$ helm delete iris-rest -n iris
CircleCI 側
Terraform と Helm クライアントを試しましたので、それらを CircleCI 側のデプロイプロセスで使用できるようにしましょう。
$ cat <root_repo_dir>/.circleci/config.ymlversion: 2.1
orbs: gcp-gcr: circleci/gcp-gcr@0.6.1
jobs: terraform: docker: # Terraform image version should be the same as when # you run terraform before from the local machine - image: hashicorp/terraform:0.12.17 steps: - checkout - run: name: Create Service Account key file from environment variable working_directory: terraform command: echo ${TF_SERVICE_ACCOUNT_KEY} > account.json - run: name: Show Terraform version command: terraform version - run: name: Download required Terraform plugins working_directory: terraform command: terraform init - run: name: Validate Terraform configuration working_directory: terraform command: terraform validate - run: name: Create Terraform plan working_directory: terraform command: terraform plan -out /tmp/tf.plan - run: name: Run Terraform plan working_directory: terraform command: terraform apply /tmp/tf.plan k8s_deploy: docker: - image: kiwigrid/gcloud-kubectl-helm:3.0.1-272.0.0-218 steps: - checkout - run: name: Authorize gcloud on GKE working_directory: helm command: | echo ${GCLOUD_SERVICE_KEY} > gcloud-service-key.json gcloud auth activate-service-account --key-file=gcloud-service-key.json gcloud container clusters get-credentials ${GKE_CLUSTER_NAME} --zone ${GOOGLE_COMPUTE_ZONE} --project ${GOOGLE_PROJECT_ID} - run: name: Wait a little until k8s worker nodes up command: sleep 30 # It’s a place for improvement - run: name: Create IRIS namespace if it doesn't exist command: kubectl get ns iris || kubectl create ns iris - run: name: Run Helm release deployment working_directory: helm command: | helm upgrade iris-rest \ --install \ . \ --namespace iris \ --wait \ --timeout 300s \ --atomic \ --set image.repository=eu.gcr.io/${GOOGLE_PROJECT_ID}/iris-rest \ --set image.tag=${CIRCLE_SHA1} - run: name: Check Helm release status command: helm list --all-namespaces --all - run: name: Check Kubernetes resources status command: | kubectl -n iris get pods echo kubectl -n iris get servicesworkflows: main: jobs: - terraform - gcp-gcr/build-and-push-image: dockerfile: Dockerfile gcloud-service-key: GCLOUD_SERVICE_KEY google-compute-zone: GOOGLE_COMPUTE_ZONE google-project-id: GOOGLE_PROJECT_ID registry-url: eu.gcr.io image: iris-rest path: . tag: ${CIRCLE_SHA1} - k8s_deploy: requires: - terraform - gcp-gcr/build-and-push-image
CircleCI 側のプロジェクトに次のようないくつかの環境変数 を追加する必要があります。
GCLOUD\_SERVICE\_KEY は CircleCI のサービスアカウントキーであり、TF\_SERVICE\_ACCOUNT_KEY は Terraform のサービスアカウントキーです。 サービスアカウントキーが _account.json_ ファイル全体の内容であることを思い出してください。
次に、変更をリポジトリにプッシュしましょう。
$ cd <root_repo_dir>$ git add .circleci/ helm/ terraform/ .gitignore$ git commit -m "Add Terraform and Helm"$ git push
CircleCI UI ダッシュボードには、次のようにすべてが正常であることが示されているはずです。
Terraform は冪等性のあるツールであり、GKE クラスターが存在する場合、「terraform」ジョブは何も実行しません。 クラスターが存在しない場合は、Kubernetes をデプロイする前に作成されます。最後に、IRIS の可用性を確認しましょう。
$ gcloud container clusters get-credentials <CLUSTER_NAME> --zone <LOCATION> --project <PROJECT_ID>
$ kubectl -n iris get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEIris-rest LoadBalancer 10.23.249.42 34.76.130.11 52773:31603/TCP 53s
$ curl -XPOST -H "Content-Type: application/json" -u _system:SYS 34.76.130.11:52773/person/ -d '{"Name":"John Dou"}'
$ curl -XGET -u _system:SYS 34.76.130.11:52773/person/all[{"Name":"John Dou"},]
まとめ
Terraform と Helm は標準の DevOps ツールであり、IRIS のデプロイと緊密に統合する必要があります。
これらはある程度の学習を必要としますが、何度か実践した後は大幅に時間と労力を節約できるようになります。
記事
Megumi Kakechi · 2024年3月20日
%Installerという特別なツールを使用すると、目的のIRIS構成を記述するインストールマニフェストを定義できることはご存じでしょうか?インストールマニフェストに作成したい IRIS 構成を記述すると、インストール中、またはターミナルやコードからマニフェストを実行した際に、構成設定が適用されます。
インストールマニフェストについては、以下の記事でご紹介しておりますので是非ご覧ください(Cachéの記事になりますがIRISでも同様です)。%InstallerでInterSystems Cachéにアプリケーションをデプロイする
こちらの記事では、実際にマニフェストで追加できる主な項目について、サンプルコードとあわせてご紹介します。今回は、以下の機能の設定例をご紹介します。
1. ライセンスファイルのコピーと適用2. ネームスペース・データベースの作成3. 構成情報(ポートやデータベースキャッシュ・メモリ設定など)の適用4. セキュリティ情報(ユーザ・サービスなど)のインポート ※同一バージョンのみ5. セキュリティ情報(ユーザ・ロールなど)の作成6. SQL情報の作成7. ジャーナル情報の作成8. ユーザ作成クラスやグローバルのインポート、メソッドの実行9. システムクラスのクラスメソッドを引数付きで実行 -バックアップリストの作成 -監査のシステムイベント設定と監査の有効化 -データベースの暗号化※Appendix:Manifest の簡単テスト手順 ※ターミナルでマニフェストを実行
★1.ライセンスファイルのコピーと適用
<!-- copy iris.key to mgr -->
<CopyFile Src="${SourceDir}\iris.key" Target="${MGRDIR}\iris.key" name="copyKey"/>
<!--Activate license key -->
<Invoke Class="%SYSTEM.License" Method="Upgrade" CheckStatus="false"/>
<!-- インストール前にコピーすることも可能
1. mkdir -p <install_dir>\mgr
2. copy cache.key <install_dir>\mgr
3. インストール実行時は適用のみ
-->
※${SourceDir} は、マニフェストで使用できる事前定義変数になります。SourceDirは、インストール実行ファイルのあるディレクトリになります。詳細は以下のドキュメントをご覧ください。マニフェストタグ内で使用できる事前定義変数
※マニフェストで使用できるタグの一覧は以下のドキュメントをご覧ください。<Manifest> タグのリスト
★2.ネームスペース・データベースの作成
管理ポータル:システム管理 > 構成 > システム構成 > ネームスペース
<Namespace Name="TEST" Create="yes" Code="TEST" Data="TEST">
<Configuration>
<Database Name="TEST" Create="yes" Dir="C:\db\test" Resource="%DB_TEST"/>
</Configuration>
<CSPApplication Url="/csp/test" Directory="C:\db\csp\test" AuthenticationMethods="64"/>
</Namespace>
さらに、ネームスペースにルーチン等インポートの場合、
<Namespace Name="TEST2" Create="yes" Code="TEST2" Data="TEST2">
<Configuration>
<Database Name="TEST2" Create="yes" Dir="C:\db\test2" MountRequired = "1"/> // 起動時にマウント
</Configuration>
<CSPApplication Url="/csp/test2" Directory="C:\db\csp\test2" AuthenticationMethods="64"/>
<Import File="C:\temp\test.xml" Flags="c"/>
</Namespace>
<!--
パラメータMountRequired が、管理ポータル>ローカルデータベースの属性「開始時にマウントが必要?」
設定値について、MountRequired 1 の値で「開始時にマウントが必要?」"はい"となります。
-->
※詳細設定については、%Installerクラス をご覧ください。 ネームスペースに設定できるパラメータは、クラスリファレンス %Installer.Namespace 、 データベースに設定できるパラメータは、クラスリファレンス %Installer.Database 、 Webアプリケーションに設定できるパラメータは、クラスリファレンス %Installer.CSPApplication で確認できます。
★3.構成情報(ポートやデータベースキャッシュ・メモリ設定など)の適用
管理ポータル:システム管理 > 構成
※Manifest ではなく、INSTALLERMANIFESTPARAMS でも設定可能です。
設定可能項目:
bbsiz
プロセスあたりの最大メモリ (KB)
globals4kbglobals8kbglobals16kbglobals32kbglobals64kb
**KBデータベースキャッシュ用メモリ (MB)
gmheap
共有メモリヒープサイズ (MB)
LibPath
LD_LIBRARY_PATH 環境変数にディレクトリを追加。UNIX® システムでのみ有効
Locksiz
ロックに使用する共有メモリの最大サイズ (KB)
MaxServerConn
ECP クライアントからの接続の最大数
Path
既定の PATH 環境変数にディレクトリを追加。UNIX® システムでのみ有効
routines
ルーチンキャッシュ用メモリ (MB)
ZFSize
$ZF の入力パラメータと出力パラメータ用に仮想メモリに割り当てられた合計バイト数
ZFString
$ZF ヒープ上の各出力バイト配列または文字列パラメーターに割り当てられたバイト数
例:INSTALLERMANIFESTPARAMS="bbsiz=512000,globals4kb=20, globals8kb=30,globals16kb=40,globals32kb=50, globals64kb=100,routines=40,gmheap=10000, LibPath=c:\libpath\,locksiz=2179648,MaxServerConn=5, Path=c:\lib\,ZFSize=2000,ZFString=3000"
<Manifest> で追加する場合は、<SystemSetting> タグで追加します。
<!-- Memory settings -->
<SystemSetting Name="Config.config.gmheap" Value="262144"/> <!-- 共有メモリヒープサイズ -->
<SystemSetting Name="Config.config.bbsiz" Value="262144"/> <!-- プロセスあたりの最大メモリ -->
<SystemSetting Name="Config.config.locksiz" Value="16777216"/> <!-- ロックに使用する共有メモリの最大サイズ -->
<SystemSetting Name="Config.Telnet.DNSLookup" Value="0FF"/> <!-- Telnet クライアントアドレスを DNS で検索可能フラグ -->
<SystemSetting Name="Config.Telnet.Port" Value="2323"/> <!-- Telnet ポート番号 -->
<SystemSetting Name="Config.config.routines" Value="128"/> <!-- ルーチンキャッシュ用メモリ(MB) -->
<SystemSetting Name="Config.config.globals8kb" Value="1024"/> <!-- 8KBデータベースキャッシュ用メモリ(MB) -->
※各設定項目については、Config.* クラスで確認できます。Config.config クラスConfig.Telnet クラス
管理ポータル:システム管理 > 構成 > 追加の設定 > 互換性
<!-- Miscellaneous settings -->
<SystemSetting Name="Config.Miscellaneous.LogRollback" Value="1"/> <!-- トランザクションロールバックをmessages.logに記録 -->
※既存インスタンスの設定一覧は、ターミナルにて以下のようにして確認できます。
%SYS>set Status=##Class(Config.Miscellaneous).Get(.Properties)
%SYS>zwrite Properties
Properties("AsyncDisconnectErr")=0
Properties("AsynchError")=1
Properties("BreakMode")=1
Properties("CollectResourceStats")=0
管理ポータル:システム管理 > 構成 > 追加の設定 > 開始
<!-- Startup settings -->
<SystemSetting Name="Config.Startup.ZSTU" Value="0"/> <!-- ユーザ定義の起動を実行しない -->
※既存インスタンスの設定一覧は、ターミナルにて以下のようにして確認できます。
%SYS>set Status=##Class(Config.Startup).Get(.Properties)
%SYS>zwrite Properties
Properties("CallinHalt")=1
Properties("CallinStart")=1
Properties("CliSysName")=""
Properties("DBSizesAllowed")=8192
Properties("DefaultPort")=1972
:
管理ポータル:システム管理 > 構成 > 追加の設定 > メモリ詳細
※既存インスタンスの設定一覧は、ターミナルにて以下のようにして確認できます。
%SYS>set Status=##Class(Config.config).Get(.Properties)
%SYS>zwrite Properties
Properties("BackoffDisabled")=0
Properties("ConsoleFile")=""
Properties("LargePagesDisabled")=0
Properties("LargePagesRequired")=0
Properties("LibPath")=""
Properties("LockSharedMemory")=0
:
★4.セキュリティ情報(ユーザ・サービスなど)のインポート ※同一バージョンのみ
管理ポータル:システム管理 > セキュリティ
★ユーザ設定※Security.Usersクラス、もしくは^SECURITYユーティリティでユーザ設定をエクスポートしたファイルを使用。 ^SECURITYユーティリティの場合 1) User setup > 6) Export users
<!-- Security settings -->
<Invoke Class="Security.Users" Method="Import" CheckStatus="true">
<Arg Value="C:\temp\Security_Users.xml" />
</Invoke>
★サービス設定※Security.Servicesクラス、もしくは^SECURITYでサービス設定をエクスポートしたファイルを使用。
<Invoke Class="Security.Services" Method="Import" CheckStatus="true">
<Arg Value="C:\temp\Security_Services.xml" />
</Invoke>
★全設定※^SECURITY ユーティリティで全設定を一括エクスポートしたファイルを使用。 12) System parameter setup > 5) Export All Security settings
<Invoke Class="Security.System" Method="ImportAll" CheckStatus="false">
<Arg Value="${SetupDir}\NameSpace\KESecurityExport.xml"/>
</Invoke>
★5.セキュリティ情報(ユーザ・ロールなど)の作成
<User
Username="Clerk1"
PasswordVar="UserPwd"
Roles="testrole"
Fullname="Data Entry Clerk"
Namespace=""
Routine=""
ExpirationDate=""
ChangePassword="0"
Enabled="1"
Comment=""/>
<Resource
Name="%accounting_user"
Description="Accounting"
Permission="RW"/>
<Role
Name="testrole"
Description=""
Resources="%Development:U,%accounting_user:RWU"
RolesGranted="%All"/>
※<User> タグの PasswordVar パラメータはユーザのパスワードを格納した変数を指定します。ユーザとパスワードの追加について※変数設定例<Var Name="UserPwd" Value="xxx"/>
★6.SQL情報の作成
管理ポータル:システム管理 > 構成 > SQLとオブジェクトの設定 > SQL
<!-- SQL settings -->
<SystemSetting Name="Config.SQL.DelimitedIds" Value="0"/> <!-- 区切り識別子サポート(なし=0) -->
<SystemSetting Name="Config.SQL.SaveMAC" Value="1"/> <!-- クエリキャッシュのソースを保持(保持する=1) -->
<SystemSetting Name="Config.SQL.LockThreshold" Value="3000"/> <!-- ロックエスカレーション閾値 -->
<SystemSetting Name="Config.SQL.DefaultSchema" Value="Test"/> <!-- デフォルトスキーマ -->
※各設定項目については、Config.SQLクラス で確認できます。
※既存インスタンスの設定一覧は、ターミナルにて以下のようにして確認できます。
%SYS>set Status=##Class(Config.SQL).Get(.Properties)
%SYS>zwrite Properties
Properties("ANSIPrecedence")=1
Properties("AdaptiveMode")=1
Properties("AllowRowIDUpdate")=0
Properties("AutoParallel")=1
Properties("AutoParallelThreshold")=3200
Properties("BiasQueriesAsOutlier")=0
Properties("Comment")=1
Properties("DBMSSecurity")=1
:
★7.ジャーナル情報の作成
管理ポータル:システム管理 > 構成 > システム構成 > ジャーナル
<!-- Journal settings -->
<SystemSetting Name="Config.Journal.CurrentDirectory" Value="xxx"/> <!-- プライマリのジャーナル・ディレクトリ -->
<SystemSetting Name="Config.Journal.AlternateDirectory" Value="xxx2"/> <!-- セカンダリのジャーナル・ディレクトリ -->
<SystemSetting Name="Config.Journal.FileSizeLimit" Value="2048"/> <!-- 新しいジャーナルファイルに切り替えるサイズ (MB) -->
※各設定項目については、Config. Journalクラスで確認できます。
※既存インスタンスの設定一覧は、ターミナルにて以下のようにして確認できます。
%SYS>set Status=##Class(Config.Journal).Get(.Properties)
%SYS>zwrite Properties
Properties("AlternateDirectory")="C:\InterSystems\IRIS\mgr\journal\"
Properties("BackupsBeforePurge")=2
Properties("CompressFiles")=1
Properties("CurrentDirectory")="C:\InterSystems\IRIS\mgr\journal\"
Properties("DaysBeforePurge")=2
Properties("FileSizeLimit")=1024
Properties("FreezeOnError")=0
Properties("JournalFilePrefix")=""
Properties("JournalcspSession")=0
★8.ユーザ作成クラスやグローバルのインポート、メソッドの実行
インストールマニフェストタグ経由で設定できない項目は、ObjectScriptでメソッドを作成し、<Invoke> タグで実行します。<Import> タグを使用すると、クラスファイルのロード/コンパイルに加え、グローバルをロードすることも可能です。
<Namespace Name="USER">
<!-- 変数の指定 -->
<Var Name="SourceDir" Value="C:\CacheInstallAcademy"/>
<Import File="${SourceDir}\quote.gof" Flags=""/>
<Import File="${SourceDir}\CustomerClasses.xml" Flags="ck"/>
<Invoke Class="User.ManifestTest" Method="setBackupList" CheckStatus="" Return="">
<Arg Value=""/>
</Invoke>
</Namespace>
例:データベースバックアップリストの作成
管理ポータル:システム管理 > 構成 > データベースバックアップ > データベースバックアップリスト
以下のようなクラスメソッドを作成し、<Invoke> タグを使用して実行します。
Class User.ManifestTest Extends %RegisteredObject
{
ClassMethod setBackupList() [ Language = objectscript ]
{
Set rc=##class(Backup.General).ClearDatabaseList() // リストのクリア
Set rc=##class(Backup.General).AddDatabaseToList("TEST") // リストに追加
Set rc=##class(Backup.General).AddDatabaseToList("TEST2")
Set rc=##class(Backup.General).AddDatabaseToList("TEST3")
}
}
※インストールマニフェストには以下のタグを追加して実行します。
<Import File="${SourceDir}\User.ManifestTest.xml" Flags="ck" />
<Invoke Class="User.ManifestTest" Method="setBackupList" CheckStatus="false"/>
例:監査のシステムイベント設定と監査の有効化
管理ポータル:システム管理 > セキュリティ > 監査
以下のようなクラスメソッドを作成します。実行方法は、上記 setBackupList() と同じです。
ClassMethod setBackupList() [ Language = objectscript ]
{
// システムイベント:"%System/%Login/LoginFailure" を有効に設定する
set Property("Enabled")=1
set status=##class(Security.Events).Modify("%System","%Login","LoginFailure",.Property)
// 監査ログを有効に設定する
// do ##class(Security.System).Get("SYSTEM",.Property2) // 一覧を参照したいとき。zwrite Property2
set Property2("AuditEnabled")=1
do ##class(Security.System).Modify("SYSTEM",.Property2)
}
例:データベースの暗号化
管理ポータル:システム管理 > 暗号化
以下のようなクラスメソッドを作成します。実行方法は、上記 setBackupList() と同じです。
ClassMethod setEncryption() [ Language = objectscript ]
{
// (1) 暗号化キーの作成
// $system.Encryption.CreateAutoEncryptionKeyOnly(キーファイル名,セキュリティレベル, Description,管理者名,パスワード)
set status=$system.Encryption.CreateAutoEncryptionKeyOnly("/usr/irishealth/mgr/autofile.key",128,"Key for automatic activation at startup",.username,.password)
// (2) 暗号化キーの有効化
// $system.Encryption.ActivateAutoEncryptionKey(キーファイル名,管理者名,パスワード,iristemp,journal,audit)
// 生成された管理名,パスワードを指定、iristemp,journal,audit を暗号化する場合は それぞれ1 を指定
set status=$system.Encryption.ActivateAutoEncryptionKey("/usr/irishealth/mgr/autofile.key",.username,.password,0,0,0)
// (3) 既存データベースの暗号化
// ##class(SYS.Database).DismountDatabase(データベースファイルパス) ; 暗号化の前にディスマウント
// ##class(SYS.Database).EncryptDatabase(データベースファイルパス) ; 暗号化
// データベースファイルの暗号化はキーの有効化の後に実施する必要があります
set status=##class(SYS.Database).DismountDatabase("/usr/irishealth/mgr/test/")
set status=##class(SYS.Database).EncryptDatabase("/usr/irishealth/mgr/test/")
}
★9.システムクラスのクラスメソッドを引数付きで実行
例:他環境よりエクスポートしたタスクスケジュールをインポートします。
※他環境からエクスポートする方法は、こちら の記事をご覧ください。
管理ポータル:システムオペレーション > タスクマネージャー > タスクスケジュール
<Invoke Class="%SYS.TaskSuper" Method="ImportTasks" CheckStatus="1" Return="">
<Arg Value="c:\temp\exportedTasks.xml"/>
</Invoke>
※Appendix:Manifest の簡単テスト手順
Manifest は、既存のインスタンスで動作確認をすることができます。以下のマニフェストは、サイレントインストール時にそのまま INSTALLERMANIFEST="C:\MyInstaller.cls" もしくは、INSTALLERMANIFEST="C:\MyInstaller.xml" のように指定することが可能です。
1. 以下のようなマニフェストを作成します。
/*------ User.MyInstaller.cls -----------------------*/
Include %occInclude
Class User.MyInstaller Extends %RegisteredObject
{
XData MyInstall [ XMLNamespace = INSTALLER ]
{
<Manifest>
<Namespace Name="TEST" Create="yes" Code="TEST" Data="TEST" Ensemble="0">
<Configuration>
<Database Name="TEST" Create="yes" Dir="${MGRDIR}test" Resource="%DB_TEST"/>
</Configuration>
<CSPApplication Url="/csp/test" Directory="${CSPDIR}test" AuthenticationMethods="64" Recurse="1"/>
</Namespace>
</Manifest>
}
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 3,
pInstaller As %Installer.Installer,
pLogger As %Installer.AbstractLogger)
As %Status [ CodeMode = objectgenerator, Internal ]
{
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "MyInstall")
}
}
※Webアプリケーションの認証方法の設定例:4=Kerberos、32=password、64=unauthenticated2. ターミナルで以下のように実行します(%SYSネームスペースで実行します)。
%SYS>Do ##class(User.MyInstaller).setup()
3. 管理ポータルで、TESTネームスペースと、TESTデータベースができているのを確認します。
記事
Tomohiro Iwamoto · 2020年10月22日
リモートや在宅での勤務が一般化しつつあります。
そのため、今までの集中型、オンサイトの開発体制を見直し、分散型の開発体制への移行を進めておられるユーザさんも多いのではないかと思います。
VSCodeを使用したIRISアプリケーションの開発が、コミュニティーを中心に広まり始めて久しいですが、Gitとの相性が良いこの開発ツールが今後さらに浸透していくことは間違いありません。あちらこちらで、その使いまわし方が語られていますが、ここでは、ソースコントロールとの関連を中心にご紹介したいと思います。
ObjectScript Extensionの使い方の基本については、こちらやこちらをご覧ください。
VSCode InterSystems ObjectScript Extensionのプロダクションリリース(V1.0.x)の配布が始まりました。
これに合わせて、今までのコミュニティーサポートに加え、InterSystemsによる公式サポートもアナウンスされています。よりいっそう安心してご利用いただけるようになりました。
目的
メインの開発ツールとしてVSCode+ObjectScript Extensionを使用している環境でのソースコード管理について、その流れを解説します。また、現時点ではVSCodeでビジュアル編集ができないInteroperability機能の編集内容(スタジオ、管理ポータルを使用します)をソースコード管理に加える方法を例示します。
前提
IRISのソースコード管理がその期待通りの動作をするためには、いくつか前提が存在します。
各利用者が専用のワークディレクトリ、ローカルレポジトリを持つ
これは、Gitの利用環境ではごく普通のことです。これを共有してしまうと、コミット内容にあわせて編集対象をステージングするというGitの基本的な操作が出来なくなります。
一時的にデバッグ用途で作成したルーチン(test1, deb2)などを誤ってソースコード管理に追加するのは避けたいものです。
VSCodeとスタジオで同一のワークディレクトリ、ローカルレポジトリを使用する
これを別にしてしまうと、同じファイルを別のローカルレポジトリにコミットするという危険を排除出来なくなります。
また、VSCode,スタジオ双方を使用した一連の修正をコミットするという操作が出来なくなります。
各利用者が編集時の接続先となる専用のIRIS環境を持つ
これを共有してしまうと、利用者どうしの修正が逐次干渉しあい、またコミット前の修正を"ダーティリード"することになるため、分散開発のメリットが損なわれます。
環境例
これら前提を満たす、2種類の環境を例示します。
1. 全てローカル
各利用者がIRISインスタンス自体をローカルPC上にインストールして個人環境として使用しながら開発を進めます。
Gitのような分散型のソースコード管理と相性が良く、スタジオのソースコントロールとの共存も容易なため、お勧めです。
2. IRIS環境を共有
全利用者が共有のIRIS環境を使用します。前提を満たすためにネームスペースとそこに紐づけるデータベース、ワークディレクトリを、各利用者ごとに用意します。この場合、分散開発は実現しません。また、後述の手間がかかるため、ソースコード管理を徹底活用したいという向きにはあまりお勧めはしません。
利用者
ネームスペース
データベース
用途
UserA
MYAPP_USERA
myapp_usera/IRIS.DAT
ソースコード
common/IRIS.DAT
共通ソースコード
data_usera/IRIS.DAT
データ
UserB
MYAPP_USERB
myapp_userb/IRIS.DAT
ソースコード
common/IRIS.DAT
共通ソースコード
data_userb/IRIS.DAT
データ
通常、VSCodeのワークディレクトリはローカルPC上ですが、スタジオや管理ポータルを併用する場合(ソースコントロールフックを使用する場合)、VSCodeのワークディレクトリもリモート上に用意し、Remote Development extensionでSSH接続します。
これはLinux向けの機能ですが、WindowsであってもSSHとWSLを導入すればVSCodeのリモート接続対象になれます。
1,2共にIRISをDockerで稼働させることで、後述するブランチの切り替え時操作も非常に楽になります。魅力的な選択子なのですが実行環境がLinuxに限定されます。
使用するソースコード管理機能
VSCodeでは標準のソースコントロール機能(Git)を使用します。
スタジオ/管理ポータルでは、編集内容を保存時にワークディレクトリに出力するこちらのソースコード管理フックを使用します。
使用方法の例
上述の「全てローカル」の場合を例にとり使用方法の流れを俯瞰します。
全体の流れは、Gitを使用した共有リポジトリパターンの典型的な開発フローと何ら変わりありませんので、利用環境に合わせて適用いただくための参考としてご覧ください。
1. リモートリポジトリを作成
開発プロジェクトProject1用に、管理者がレポジトリ名:Project1, ブランチb1を作成。
cd \var\git
git clone https://github.com/IRISMeister/Project1.git
cd Project1
git checkout -b b1
git push --set-upstream origin b1
2. フォルダ構成の決定
決まりはありません。ここでは、下記のようなフォルダ構成とします。
フォルダ名: Project1 通常レポジトリ名と一致する。
C:\var\git\Project1 ローカルレポジトリ(.gitフォルダ)が存在
C:\var\git\Project1\... IRISと直接関係のないファイル(他プログラミング言語、リソース、Docker関連など)を配置
C:\var\git\Project1\src IRIS関連ファイル(cls, mac, incなど)を配置
3. 参加各位のローカルフォルダにgit clone
ブランチをb1に切り替えて作業開始
cd \var\git
git clone https://github.com/IRISMeister/Project1.git
cd Project1
git checkout b1
4. ツールの初期設定
VSCodeでの設定
単にそのワークディレクトリを開くだけです。
cd \var\git\Project1
code .
コンパイル・テスト実行環境として使用するための環境として、ローカルIRISのMYAPP(任意です)ネームスペースを接続先に指定します。
VSCodeでは、.VSCode/settings.jsonに下記のような接続情報を設定します。
"objectscript.conn": {
"active": true,
"host": "localhost",
"port": 52773,
"ns": "MYAPP",
"username": "SuperUser",
"password": "SYS"
}
よりセキュアなInterSystems Server Managerを使用いただきたいところですが本題から逸れてしまうので割愛いたします
スタジオでの設定
BPM,DTL,Ruleの編集内容をソースコントロールに含める必要がある場合、スタジオになんらかのソースコントロールフックの導入が必要です。前述のソースコントロールフック(%ZScc.Basic)を導入・有効化し、ファイル出力先として、ワークディレクトリを指定します。
Set $NAMESPACE="MYAPP"
Set ^ZScc("Basic","LocalWorkspaceRoot")="c:\var\git\Project1\"
Set ^ZScc("Basic","Src")="src"
その後、通常の接続操作でMYAPPネームスペースに接続します。これで当該ネームスペース上にてソースコントロールが有効化します。また、この設定は、管理ポータルでのBPM,DTL,Rule編集画面でも有効になります。
5. 開発作業
git pull - コンフリクト解消 - ローカルIRISへのImport - 開発作業 - git add/commit - git push
この繰り返しになります。
主な編集作業はVSCodeで行います。Interoperabilityのプロダクション構成,BPL,DTL,Ruleだけは、スタジオもしくは管理ポータルで編集作業を行います。
プロダクション構成,BPL,DTL,Ruleもビジュアルな編集ではなく、ソースコードとしての編集であればVSCodeで可能です
スタジオでのソースコード管理コマンドの実行方法はメニュー操作になります。
また、ソースコントロールフックの有効化を行うと、管理ポータルのBPL,DTL,Rule編集画面などにソースコード管理ボタンが提供されます。
左のアイコンでメニュー操作、右のアイコンで出力の確認を行います。
ただし、本例で使用する%ZScc.Basicのフックは、ソースコード保存時に、自動的にワークディレクトリに対象をエクスポートするだけですのでメニュー操作はほぼ不要です。
Git用のソースコントロールフック(%ZScc.Git)を選んだ場合、各Gitコマンドの発行が可能になりますが、本稿の対象外です。これとは別に、スタジオ単体利用時により特化したソースコード管理フックも公開されています。
開発作業のイメージ
他の利用者による修正を反映、コンフリクトがあれば解消します。修正内容をローカルIRISに反映します。画像(2)
ローカルIRISを使用しながら開発・単体テストを実行します。画像(3)
(必要に応じて)スタジオや管理ポータルでBMP,DTL,Ruleを新規作成・編集します。画像(3,4)
これらの変更はVSCode上で、未ステージング状態の変更要素として認識されます。
適宜ローカルのレポジトリにコミットします。
VSCode(お勧め)、コマンドラインで実行します。画像(5)
適宜リモートレポジトリにプッシュします。
VSCode(お勧め)、コマンドラインで実行します。画像(6)
6. 自動化テスト
継続的にテスト実施するような環境を使用して、リモートリポジトリのb1ブランチのソースコードをテストします。 画像(7)
Dockerを使える(プロダクション環境がLinux環境)と楽です。画像(8,9)
こちらにGitHub Actionを使用して、IRISベースのDockerイメージを作成する例があります。
プロダクション環境がWindowsの場合はチャレンジングな項目になります。可能性としては、WindowsでDOSやPowerShellを起動可能なCI/CDツールとIRISの無人インストール機能を組み合わせるなどして実現していく事になると思います。画像(10)
7. リリース作業
管理者がb1ブランチをmasterブランチにマージします。画像(11)
8. 新規リリースに向け、b2ブランチを作成。以降、繰り返し。
ブランチとネームスペースについて
シンプルな構造(ビルドに必要な全てのユーザ作成のソースコードが単一のデータベース上に存在、ブランチはmasterのみ)の採用が可能な小規模での開発が可能な場合は、これらを気にする必要はありません。
ネームスペースが指し示すソースコード保存用のデータベース(ルーチンのデフォルトデータベース)に関して配慮が必要です。
ネームスペースとソースコードの保存場所は、1対1とは限りません。パッケージマッピングなどにより複数のデータベースに跨っている可能性があります(共通関数などを別個のデータベースに配置している場合など)。これら共通関数は、使用するプロジェクトとは切り離してソースコード管理される可能性もあります。
そのことを考慮すると、ブランチ切り替えの際に、単純にネームスペースで認識できる既存のソースコードを「全部削除」して、切り替わったソースコードと置き換える、というオペレーションでは、どうしても、削除もれや削除し過ぎといったミスや無駄な再コンパイル発生を排除できません。
少々手間ではありますが、各ブランチに対応するソースコード保存専用のデータベースを個別に用意しておいて、ブランチを切り替える際には、ネームスペースのメインのソースコードの保存場所も切り替えるのが最も安全だと思います。
IRIS環境を共有する場合は、さらにこれらが各利用者ごとに分かれますので、ブランチ数×利用者数の数だけデータベースを作成することになり、かなりの手間となります。
ブランチ
ネームスペース
データベース
用途
master
MYAPP
myapp/master/IRIS.DAT
ソースコード
CommonPackage/IRIS.DAT
共通ソースコード
DATA/IRIS.DAT
データ
b1
MYAPP
myapp/b1/IRIS.DAT
ソースコード
同上
共通ソースコード
同上
データ
b2
MYAPP
myapp/b2/IRIS.DAT
ソースコード
同上
共通ソースコード
同上
データ
一方、開発時のIRISの稼働環境として、リモートレポジトリを使用して焼いたDockerイメージを使用すれば、切り替え操作は使用するコンテナイメージの選択という簡単な操作で済む(利用者から隠ぺいされる)ため、手間(なによりもオペミス)が大幅に削減できます。
IRIS DockerイメージはLinux(Ubuntu)ベースですので、プロダクション環境もLinuxである必要があります
ブランチ
イメージ名
ネームスペース
データベース
用途
データベース保存場所
master
MYIRIS:master
MYAPP
myapp/IRIS.DAT
ソースコード
イメージ内
CommonPackage/IRIS.DAT
共通ソースコード
イメージ内
DATA/IRIS.DAT
データ
外部データベース
b1
MYIRIS:b1
MYAPP
同上
ソースコード
イメージ内
同上
共通ソースコード
イメージ内
同上
データ
外部データベース
b2
MYIRIS:b2
MYAPP
同上
ソースコード
イメージ内
同上
共通ソースコード
イメージ内
同上
データ
外部データベース
IRIS環境を共有した際の注意点
同一のネームスペースを複数の利用者が共有した場合、誰かの修正を他の人がVSCode経由で上書きしようとすると、ObjectScript Extensionから警告を受け、そのコンフリクト解消のための選択を促されます。
小規模開発であれば、これを使用して開発を進めることも可能です。VSCodeには、スタジオの操作環境に近い、サーバサイドを直接編集するモードもありますので、その利用を検討されるのも良いかもしれません。
このモードではワークディレクトリが使用されないためVSCodeのGitでIRISのソースコード管理をすることが出来ません。
記事
Megumi Kakechi · 2021年4月7日
これは InterSystems FAQ サイトの記事です。ファイル入出力処理を行うには、ライブラリクラスを利用する方法が便利です。
ライブラリクラスを使用する以外には、Open/Use/Close コマンドを使用する方法もあります。<※1>
ファイル入出力処理には、%Library.Fileクラス、%Stream.FileCharacter/%Stream.FileBinary を使用します。
簡易例やプロパティ/メソッド詳細は、以下ドキュメントをご参照ください<※2>。クラスリファレンス【IRIS】クラスリファレンス
【補足】%Libraryパッケージは、クラス定義構築基盤として利用するクラスが多いためパッケージ名を省略することができます。(%Library.File は %File として利用できます)
【A】%Fileクラスを利用する方法
%Fileクラスには、ファイル入出力操作の他に、ディレクトリ作成(CreateDirectory()など)/存在チェック(Exists())/OS非依存でファイルパス取得(NormalizeFilename()など)が行える様々な便利メソッドを用意しています。
ファイル出力処理手順は以下の通りです。
(1) ファイル用オブジェクトを作成
ファイル名をフルパスで指定しながらファイル用オブジェクトを作成します。
set file=##class(%File).%New("c:\kit\text.txt")
(2) ファイルを新規書き込みモードを指定しながらオープン N:新規作成、W:書込み、S:ストリーム形式
set st=file.Open("NWS")
【ご参考】指定の文字コード(例えば UTF-8)でファイル入出力を行う場合は、Kパラメータを使用します。
set st=file.Open("NWSK\UTF8\")
(3) ファイルへの書き込み
Write()メソッド:現在の位置に引数で指定した値を出力します。WriteLine()メソッド:現在の位置に引数で指定した値を改行付きで出力します。
set st=file.WriteLine("1行目")
set st=file.WriteLine("2行目")
set st=file.WriteLine("3行目")
(4) ファイルをクローズ
do file.Close()
(5) オブジェクト消去
kill file
ファイル入力処理手順は以下の通りです。
(1) ファイルオブジェクトを作成
set file=##class(%File).%New("c:\kit\text.txt")
(2) ファイルを読み込みモードを指定しながらオープン R:読込、S:ストリーム形式
set st=file.Open("RS")
(3) AtEndプロパティに 1 が設定されるまでファイルの読み込み
Read()メソッド:引数に指定した長さまで読み込み ReadLine()メソッド:1行ずつ読み込み ※ AtEndプロパティはファイルの最後(EOF)を検出すると 1 が設定されるプロパティです。 【補足】ストリームの先頭位置に戻るには Rewind()、後方への移動は MoveToEnd() を使用します。
while '(file.AtEnd){ write file.ReadLine(),! }
(4) ファイルをクローズ
do file.Close()
(5) オブジェクト消去
kill file
【B】%Stream.FileCharacterを利用する方法
%Fileと異なる点は、ファイルオープン時にパラメータを使用しなくても利用できる点です。ファイル出力処理手順は以下の通りです。
(1) ファイルオブジェクトを作成
set file=##class(%Stream.FileCharacter).%New()
(2) (1)で作成したオブジェクトのFilenameプロパティにオープン対象のファイルをフルパスで指定
set st=file.LinkToFile("c:\kit\text.txt")
【ご参考】指定の文字コードでファイル入出力を行う場合は、TranslateTableプロパティを利用します。
set file.TranslateTable="UTF8"
(3) ファイルへの書き込み
%File()と同様にWrite()/WriteLine()を使用します。
【補足】書き込み時、ストリームの後方へ位置を移動すると追記書き、先頭へ移動すると新規書き込みとして動作します。 (先頭位置から書き込みを行うと、以前登録のあった内容はクリアされ新規書き込みとして扱われます。)
後方移動には MoveToEnd() を使用します。
先頭へ戻るには Rewind() を使用します。
(4) ファイルの保存
set st=file.%Save()
(5) オブジェクト消去
kill file
<※1>コマンドでファイル入出力処理を行う方法は、以下ドキュメントをご参照ください。シーケンシャルファイルの入出力について【IRIS】シーケンシャルファイルの入出力について
<※2> クラスリファレンス(=クラスドキュメント)の開き方は以下の通りです。
[ランチャー] > [ドキュメント] > [クラスリファレンス]スタジオから開く方法は以下の通りです。スタジオの表示メニュー > [クラスドキュメントの表示]
以下のトピックもあわせてご覧ください。
文字コードを変換するときに利用できる変換テーブル名は何ですか?