これは、InterSystems FAQサイトの記事です。
これは、MS-ACCESSの制限事項です。
MS-ACCESSは、インデックスが32個より多いテーブルにはリンクできません。
この問題を回避する方法として、直接テーブル(クラス)をリンクするのではなく、VIEWを使用する方法があります。
これは、InterSystems FAQサイトの記事です。
これは、MS-ACCESSの制限事項です。
MS-ACCESSは、インデックスが32個より多いテーブルにはリンクできません。
この問題を回避する方法として、直接テーブル(クラス)をリンクするのではなく、VIEWを使用する方法があります。
最近はパブリッククラウド環境でIRISを使ってアプリケーションを構築されるお客様が多数で、IRISのセキュリティ機能についてご質問を受ける機会が増えました。実際の運用で始めてセキュアなIRISを操作するよりも経験した方が良いかもと考え、その第一歩としてインストール時の初期セキュリティ設定には普段からロックダウンを指定するのはいかがでしょうか。通常セキュリティ設定の違いおよび注意点をマニュアルを参照しながらリストアップしました。
パスワードの最小文字数が8文字になります。またSQLのルートユーザとして作成される_SYSTEMユーザは運用開始前の無効化を推奨しているように、高いセキュリティレベルが必要な環境では不要あるいは一時的に同等の権限を付与したユーザを作成して対応可能ではないでしょうか。
2. サービスプロパティ
Use許可のPublicが不可、とはIRISにアクセスする手段を明示的に許可しないと使えないことを意図しています。予期せぬユーザーがODBCでアクセスしたり、管理コンソールを使用したりと言った事故を防ぐ有効な手段です。
また初期状態で有効化されているサービス、WebGatewayに加えてWindowsの場合Console、非WindowsではTerminalと必要最小限になっています。
以前、Azure用にOAouth2クライアントをセットアップする記事を書いた時に思ったのですが、各IdPはサンプルコードとしてPythonコードや専用のモジュールを提供しているので、それがそのまま使用できれば効率が良いのにな、と思いました。
IRISが埋め込みPython機能としてWSGIをサポートしたことにより、これが簡単に実現しそうなので、その方法をご紹介したいと思います。
今回は、IdPとしてOKTAを使用してAuthorization Codeフローを行います。
参考までに、今回使用した環境を後半に記載しています。
コンテナ化してありますので、コンテナのビルド環境をお持ちの方は、下記を実行してください。
git clone https://github.com/IRISMeister/iris-okta-oidc-wsgi
cd iris-okta-oidc-wsgi
python/.env.templateをpython/.envという名前でコピーを作成して、OKTAで得られる設定値を指定してください。
AUTH0_CLIENT_ID="0oaxxxxxxx"
AUTH0_CLIENT_SECRET="qUudxxxxxxxxxxx"
AUTH0_DOMAIN="dev-xxxxx.okta.com/oauth2/default"
AUTH0_CLIENT_ID,AUTH0_CLIENT_SECRETは後述の「アプリケーション追加」で使用する画面から取得できます。 AUTH0_DOMAINは、後述の「カスタムSCOPE追加」で使用する画面から取得できる発行者URIを設定します。
docker compose build
docker compose up -d
下記でIRISの管理ポータルにアクセスできます。
http://localhost:8882/csp/sys/%25CSP.Portal.Home.zen
ユーザ名:SuperUser, パスワード:SYS
まずは、純粋なWSGI環境での実行を行って、設定が正しくできているかを確認します。コードはこちらを使用しました。
元々はAuth0用ですが、ほぼそのままでOKTAでも使用できました
下記のコマンドでFlaskを起動します。
docker compose exec iris python3 /usr/irissys/mgr/python/run.py
ブラウザでメインページにアクセスしてください。
http://127.0.0.1:8889/ ではリダイレクトに失敗します。
「Login」をクリックするとOKTAのログイン画面が表示されます。OKTAサインアップ時に使用した多要素認証(スマホアプリ)を使用してログインしてください。
うまく動作した場合は、取得したトークンの情報等が表示されます。namespace: USERと表示されている通り、IRISへのアクセスも行っています。
Welcome Tomohiro Iwamoto!
Logout
{
"access_token": "eyJraWQiOi.....",
"expires_at": 1731482958,
"expires_in": 3600,
"id_token": "eyJraWQ......",
"scope": "email user/*.* profile openid",
"token_type": "Bearer",
"userinfo": {
"amr": [
"mfa",
"otp",
"pwd"
],
"at_hash": "3cRg3plSvDPqGUwEBzefoA",
"aud": "xxxxx",
"auth_time": 1731477799,
"email": "iwamoto@intersystems.com",
"exp": 1731482958,
"iat": 1731479358,
"idp": "xxxxxxxxxx",
"iss": "https://dev-xxxxxx.okta.com/oauth2/default",
"jti": "ID.Z0icZKkP61n3WDLgD08q3QxJ4Ags6_rwhrqFX3lAUjs",
"name": "Tomohiro Iwamoto",
"nonce": "DYrD0GKQPyXuT6Fni1So",
"preferred_username": "iwamoto@intersystems.com",
"sub": "xxxxxx",
"ver": 1
}
}
namespace: USER
「Logout」をクリックするとOKTAからのログアウトが実行され、最初のページにリダイレクトされます。
ブラウザでIRIS+WSGI用のメインページにアクセスしてください。以降の操作は同じです。
同じFlask用のコードを使用していますので、全く同じ動作になります。
これは「IRIS+WSGIで何が可能になるか?」という問いと同じですが、本トピックに限定すると、例えばbearer tokenであるアクセストークンを、cookie(flaskのsessionの仕組み)ではなく、IRISのDB上に保存する、他のCSP/RESTアプリケーションに渡す、という応用が考えられます。
元々、CSPやIRISのRESTで作成したアプリケーションがあって、そこにIdP発行のアクセストークンを渡したい、といった用法に向いているアプローチかと思います。
また、IRISでWSGIを実行することにより、gunicornのような運用レベルのWSGI用ウェブサーバを別途立てる必要がなくなります。
今回使用した環境です。
こちらのトライアル環境を使用しました。若干画面が変わっていましたが、サインアップ手順はこちらを参考にしました。登録の際にはMFA(多要素認証)としてスマホが必要です。
ログインすると、次のようなメイン画面が表示されます。

以降、アプリケーション追加、カスタムSCOPE追加、認証ポリシー設定などを行っています。
メニューのアプリケーション->アプリケーションで、flask-code-flowという名称でアプリケーションを追加します。

「一般」タブのクライアント資格情報のは下記のようになります。

一般設定は下記のようになります。

ログイン設定は下記のようになります。複数の登録がありますが、これは実行環境に合わせて各々オリジン(ブラウザで指定するURL)が異なるためです。

ログアウト設定は下記のようになります。複数の登録がある理由はログイン設定と同じです。

「サインオン」タブの設定は下記のようになります。サインオン方法としてOpenID Connectが指定されます。

「サインオン」タブの下のほうに「ユーザー認証」というセクションがありますので、そこのポリシーの詳細リンクを押して「認証ポリシー」画面に遷移します。
「認証ポリシー」では、ルールはそのままで、アプリケーションには追加したアプリケーションを「アプリを追加」で追加登録します。

「割り当て」タブのクライアント資格情報のは下記のようになります。

リソースサーバ用にカスタムのSCOPEを追加します。「セキュリティ」->「API」でdefaultを編集して、オーディエンスとして適当なURL(ここでは http://localhost/csp/healthshare/sc/fhir/r4 )を設定します。

defaultをクリックすると下記の画面に遷移します。

「スコープ」タブを選択し、「スコープを追加」を押してカスタムSCOPEを追加します。ここではuser/*.*というスコープを追加しました。

アクセスポリシーを追加します。「セキュリティ」->「API」で認可サーバ:defaultを選択します。「アクセスポリシー」タブを選択し、default policyにアプリケーションを追加します。

「ルールを追加」を押して、新しいルールを追加します。以下のような設定にしました。

開発者の皆さん、こんにちは!
2回目の開催となったミートアップですが、11月8日(金)に無事、開催を終えることができました。ご参加いただきましたメンバーの皆様、ありがとうございました!
今年のミートアップでは、ワークショップを開催してみました。13時半~17時半の約4時間(!)もの間、ご持参いただいたパソコンでもくもくと進めていただいていたように思えます。
ミートアップのワークショップで使用した内容は、全て以下リポジトリに公開しております。
👉https://github.com/Intersystems-jp/meetup2024WorkShop
以下、写真を添えながら当日の様子を少しご紹介します。
第1部は、ワークショップを合計5種類用意いたしました。
「Python入門」、「Embedded PythonでIRISデータにアクセスしよう」までは、全員一緒のタイミングで説明を聞いていただきながら、内容を確認するワークショップを行いました。
10分間の休憩を挟んだ後は、ご用意しました3種のワークショップ:「WSGI-Flask」「NiceGUI+SQL Alchemyを使ってアプリケーションをつくってみよう」「機械学習で手書き数字の識別に挑戦」のなかから好きな内容にチャレンジいただきました。
ご好評いただいておりますインターシステムズ開発者向けウェビナー、今年最後の回は以下の通り開催いたします。
お忙しい時期ではありますが、皆様のご参加をお待ち申し上げております。
テーマ:「IRISで実現する極小オーバヘッドの IoTエッジサーバー」
日時:2024年12月10日(火)13時30分~14時
【概要】
IoTデバイスで発生するデータをサーバに送信して、アラートを管理する、後の利活用のためにデータベースに保存する、といったシナリオは多くの分野で見受けられます。その際、伝送するデータのフォーマットは、様々な処理系で利用可能で、データサイズが小さく、スキーマ定義が容易であることが望ましいです。
データフォーマットは、全体のパフォーマンスに影響を与える多数ある要素のひとつにすぎませんが、最もソフトウェアが貢献できる要素でもあります。本セミナでは、通信プロトコルとしてMQTT, メッセージフォーマットとしてApache Avro、DBとしてIRIS、コードとして(埋め込み)Pythonを使用した実行環境を「シンプルな構成」で実現する例をご紹介します。
VS Code には強力なスニペット機能が備わっており、Marketplace は開発者がスニペットを公開して使用できる方法を提供しています。 ただし、Marketplace での公開は面倒である上、InterSystems コーダーを対象としたスニペットは、現実的には Marketplace の訪問者のごく少数にしか興味を持たれません。
このことから、oex-vscode-snippets-template を作成する発想を得ました。InterSystems 開発者向けのコードスニペットを公開するための専用リポジトリを作成する GitHub テンプレートです。
また、oex-vscode-snippets-example というスニペットサンプルのリポジトリも Open Exchange で公開され、IPM / ZPM を使ってインストールされます。 以下のようにして、ネームスペースにパッケージをインストールします。
zpm "install vscode-snippets-John.Murray-example"
次に Server Manager と InterSystems ObjectScript 拡張機能を使用してそのネームスペースのサーバーサイドを編集する際は、コード補完(Ctrl+Space)リストに TODO コメントを挿入するための複数のスニペットが含まれるようになります。
oex-vscode-snippets-template は 2023 Grand Prix に出品中です。 気に入っていただけたら投票してください。 フィードバックもお待ちしています。
※ 本内容は随時更新予定です。ワークショップに必要な内容をご覧いただき、参加のご検討などしていただければ幸いです。
Pythonワークショップにご参加いただく場合は、事前に以下の環境をご用意ください。
これは InterSystems FAQ サイトの記事です。
ジャーナルファイルのサイズが大きすぎて、管理ポータルで検索やフィルタリング等できない場合、以下の2つの方法で参照することができます。
① ^JRNDUMP ユーティリティを使用する方法
② プログラムで参照する方法
============================================================
① ^JRNDUMP ユーティリティを使用する方法 例えば、グローバル参照 ^ABC を含むジャーナルファイルのすべてのレコードを選択する場合は、以下のようになります。
※以下、すべてのコマンドは %SYS ネームスペースで実行してください。
DO SELECT^JRNDUMP("C:\MyCache\mgr\journal\YYYYMMDD.001","","","^ABC",1)
グローバル参照 ^ABC に完全に一致するレコードのみを選択する場合は、以下のようになります。
DO SELECT^JRNDUMP("C:\MyCache\mgr\journal\YYYYMMDD.001","","","^ABC",0)
グローバル ^ABC に対するローカルの Set 処理のレコードのみを選択する場合は、以下のようになります。
Studio で最も便利な機能の 1 つにコードスニペットがあります。
以下は、スニペットを VSCode に追加する方法です。
以下は、一般的な手順です。
1. ファイル - 設定 - ユーザースニペットに移動し、objectscript を選択します。
2. スニペットを追加します。以下に例を示します。
"SQL Statement": {
"prefix": ["sql"],
"body": ["#dim rs As %SQL.ISelectResult",
"set rs = ##class(%SQL.Statement).%ExecDirect(,\"SELECT * FROM\")",
"while rs.%Next() {",
"\twrite rs.ID, !",
"}"]
}要素は以下のように定義されています。
さらにスニペットには、以下のようにプレースホルダーも含められます。
"Method": {
"prefix": ["method"],
"body": ["set sc = ##class(${1:class}).${2:method}()"]
}
このスニペットを挿入すると、最初のプレースホルダーの開始点に自動的に移動します。プレースホルダーは <TAB> でスクロールできます。
コーディングをお楽しみください!
開発者の皆さん、こんにちは!
第2回 技術文書ライティングコンテスト に応募された作品のボーナスポイント獲得状況をお知らせします📣(順位発表は11月1日を予定しています)
| 記事 | EmbeddedPythonに関連する記事(4点) | 生成AIに関連する記事(4点) | FHIRに関連する記事(3点) | コードの記述が含まれる記事(2点) | 動作するコードサンプルをGitリポジトリへ公開する(5点) | 記事の内容に関連した画面キャプチャや図を貼る(1点) | 合計点 |
|---|---|---|---|---|---|---|---|
| FHIR Object Modelを使ったInteroperability開発 | ✅ | ✅ | ✅ | ✅ | 11 | ||
| UnitTest(ユニットテスト)の自動化について考察 |
✅ |
✅ |
6 |
||||
| IRIS環境設定の自動化について~インストールマニフェストの利用~ | ✅ | ✅ | ✅ | ✅ | 12 | ||
| Embedded Python を利用する時の注意点 |
✅ |
✅ | ✅ | 7 | |||
| IRIS開発における生成AIの活用について | ✅ | ✅ | ✅ | 7 | |||
| SourceControlを用いた自動ソースチェックツールについて | ✅ | ✅ | ✅ | 8 |
コミュニティの皆さん、こんにちは!
開発者コミュニティ AI 懸賞企画 お楽しみいただけましたか? まだまだ改良の必要がありそうですが、DC AIを利用して何か新しい回答が得られていることを願っています。
この投稿では、優勝者を発表します!🎊(抽選の様子を動画でご紹介しています。ぜひご覧ください!)
これは、InterSystems FAQサイトの記事です。
InterSystemsでは、以前は、OSの特定リリースに対して製品の検証は実施しておりませんでしたが、
バージョン2022.1以降においては、OSのマイナーバージョンに対する検証を実施しております。
最新のサポートプラットフォームの情報については、下記の記事をご確認ください。
※2024年12月現在の最新情報
InterSystems サポートプラットフォーム最新情報 Q4-2024
★最新情報は開発者コミュニティにて随時発信いたしますので、以降の状況については、上記記事の継続記事をご確認ください。
(ご参考)
以前の状況についてはこちら
Windowsのサービスパックの適用に対する製品サポートについて
Windows Serverのリリースの違いは、サポートバージョンに影響しますか?
これは、InterSystems FAQサイトの記事です。
インターシステムズは、特定のオペレーティングシステムのパッチやサービスパックに対して、製品の検証は実施しておりません。
これらの保証については、オペレーティングシステムベンダーが互換性を保証する限りにおいて、提供されます。
まれなケースで、インターシステムズの製品を稼動するために、特定のパッチやサービスパックを必要とするケースもあります。
その様な特別な状況の詳細は、サポートサーバプラットフォームに明記しています。以下ドキュメントをご参照ください。
明記されていない場合には、オペレーティングシステムベンダーが互換性を保証する限りにおいて、パッチやサービスパックもサポート対象となります。
※2022.1以降のバージョンにおいては、OSのマイナーバージョンにおいても検証を実施しております。
InterSystems IRIS、InterSystems IRIS for Health、HealthShare Health Connect の2つのメンテナンスリリースがリリースされました。
✅ 2023.1.5
リリース 2023.1.5 は、以前のリリース 2023.1.x のバグフィックスを提供します。
詳細な変更リストとアップグレード・チェックリストは、以下のページにあります :
✅ 2024.1.2
リリース 2024.1.2 は、以前のリリース 2024.1.x のバグフィックスを提供します。
詳細な変更リストとアップグレード・チェックリストは、以下のページにあります :
CI/CD シリーズの新しい章へようこそ。ここでは、InterSystems テクノロジーと GitLab を使用したソフトウェア開発の様々な可能なアプローチを取り上げています。 今回も相互運用性について説明を続けますが、特に相互運用性デプロイの監視に焦点を当てます。 まだアラートをすべての相互運用性プロダクションにセットアップしていない場合は、それをセットアップしてエラーとプロダクションの状態についての一般的なアラートを取得できるようにしてください。
非活動タイムアウトは、すべての相互運用性ビジネスホストに共通する設定です。 ビジネスホストは、「Inactivity Timeout(非活動タイムアウト)」フィールドに指定された秒数以内にメッセージを受信しない場合に非アクティブステータスになります。 プロダクションの監視サービスはプロダクション内のビジネスサービスとビジネスオペレーションのステータスを定期的に確認し、非活動タイムアウト期間内にアクティビティがない場合にその項目を「非アクティブ」にマークします。
デフォルト値は 0(ゼロ)です。 この設定が 0 である場合、ビジネスホストはアイドル状態がどれほど続いても Inactive にマークされることはありません。
これはアラートを生成し、構成されたアラートと合わせてプロダクションの問題に関するリアルタイム通知を可能にするため、非常に便利な設定です。 ビジネスホストがアイドル状態である場合、プロダクション、統合、またはネットワーク接続に調べる価値のある問題がある可能性があります。 ただし、ビジネスホストには一定時間の非活動タイムアウトを 1 つしか設定できないため、夜間、週末、休日などのトラフィックの少ない既知の期間中に不要なアラートを生成する可能性があります。 この記事では、動的な非活動タイムアウトを実装するためのいくつかのアプローチを説明します。 機能する例(現在ある顧客サイトの本番環境で実行しているもの)を紹介していはいますが、この記事は独自の動的な非活動タイムアウトの実装を構築するためのガイドラインを紹介することを目的としているため、ここに提案するソリューションを唯一の代替手法と見なさないようにしてください。
相互運用性エンジンは、各ビジネスホストをサブスクリプトとして、最新のアクティビティのタイムスタンプを値として含む特殊な HostMonitor グローバルを維持しています。 非活動タイムアウトを使用する代わりに、このグローバルを監視し、HostMonitor の状態に基づいてアラートを生成することにします。 HostMonitor は、非活動タイムアウト値が設定されているかに関係なく維持されます。常にオンの状態です。
まず初めに、以下のようにして HostMonitor グローバルを反復処理します。
Set tHost=""
For {
Set tHost=$$$OrderHostMonitor(tHost)
Quit:""=tHost
Set lastActivity = $$$GetHostMonitor(tHost,$$$eMonitorLastActivity)
}
監視サービスを作成するには、各ビジネスホストに対して以下のチェックを実行する必要があります。
すると、コードは以下のようになります。
Set tHost=""
For {
Set tHost=$$$OrderHostMonitor(tHost)
Quit:""=tHost
Continue:'..InScope(tHost)
Set lastActivity = $$$GetHostMonitor(tHost,$$$eMonitorLastActivity)
Set tDiff = $$$timeDiff($$$timeUTC, lastActivity)
Set tTimeout = ..GetTimeout(tDayTimeout)
If (tDiff > tTimeout) && ((lastActivityReported="") || ($system.SQL.DATEDIFF("s",lastActivityReported,lastActivity)>0)) {
Set tText = $$$FormatText("InactivityTimeoutAlert: Inactivity timeout of '%1' seconds exceeded for host '%2'", +$fn(tDiff,,0), tHost)
Do ..SendAlert(##class(Ens.AlertRequest).%New($LB(tHost, tText)))
Set $$$EnsJobLocal("LastActivity", tHost) = lastActivity
}
}
カスタムロジックを実際に格納している InScope と GetTimeout メソッドを実装したら、完了です。
この例では、日中のタイムアウト(Day Timeout、これはビジネスホストごとに異なる可能性がありますが、デフォルト値があります)と夜間のタイムアウト(Night Timeout、追跡されているすべてのビジネスホストに共通)があるため、ユーザーは以下の設定を指定する必要があります。
OperationA=120$SYSTEM.SQL.Functions.DAYOFWEEK(date-expression) を実行してチェックします。 デフォルトでは、返される値は以下の曜日を表します: 1 — 日曜日、2 — 月曜日、3 — 火曜日、4 — 水曜日、5 — 木曜日、6 — 金曜日、7 — 土曜日。完全なコードを確認できますか、特に興味深い点はないと思います。 単に InScope と GetTimeout メソッドを実装しているだけです。 他の基準を使用して、InScope と GetTimeout メソッドを必要に応じて調整できます。
言及する問題は 2 つあります。
上記のソリューションを実装する前に、以下のアプローチを探りました。
InactivityTimeout 設定を変更するビジネスサービスを作成する。 当初、この方法で進もうと考えましたが、主に、InactivityTimeout 設定を変更するたびに影響のあるすべてのビジネスホストを再起動しなければならないなど、多数の問題に遭遇しました。InactivityTimeout である場合にアラートを送信する代わりにアラートを抑止するというツールを追加する。 Ens.MonitorServoce からの非活動アラートが LastActivity を更新するため、カスタムアラートプロセッサーからは「実際の」最終アクティビティタイムスタンプを取得する方法がわかりません(Ens.MessageHeader をクエリする以外で)。 また「夜間」である場合、ホストの状態を OK に戻し、まだ夜間の InactivityTimeout でなければアラートを抑止します。Ens.MonitorService を拡張する。これは、OnMonitor コールバック以外は可能に思えませんでしたが、他の目的には役立ちます。必ずアラートをすべての相互運用性プロダクションにセットアップして、エラーやプロダクションの状態についての一般的なアラートを取得します。 静的な非活動タイムアウトで十分でなければ、動的な実装を簡単に作成できます。
開発者の皆さん、こんにちは!
11月8日(金)は開発者コミュニティのミートアップ開催日です。申し込みはお済でしょうか??
昨年、大阪で初回ミートアップを開催した際、コミュニティメンバー同士での交流をとても楽しみにしてくださる方が多かった印象を受けました。
そこで、今年はメンバー同士の結束をより高められたら・・と思い、ミートアップ2024Tシャツを作成しました!
開発者の皆さん、こんにちは!
🖋 InterSystems Japan 技術文書ライティングコンテスト2024:IRISに関連した記事 🖋の応募受付期間が終了し、残るは投票のみとなりました!
第2回目のコンテストは✨6作品✨の応募がありました!
コミュニティメンバーは「イイネ」
をクリックすることで投票できます。2024年10月31日(木)23時59分59秒 まで投票受付中です。
素晴らしい6作品のなかから、🔥これだ!🔥と思う記事の「イイネ」をクリックしてください。
以下、投票方法についてご案内します。
(1) 開発者コミュニティにログインする。
ログインアカウントをお持ちでない方は、コミュニティページ上部の
(下図赤枠)をクリックし、アカウント登録を行って下さい。
クリックした後の流れは「アカウント作成方法」の記事をご参照ください。
(2) 投稿記事一覧ページに移動します。
投稿記事を👀じっくり読みます👀
(3) 「いいね」をクリックします。
投稿記事一覧ページ上でクリックする場合はそれぞれの投稿画面左下にある
のマークをクリックします。クリック前はグレーの表示ですがクリック後
に変わります。
各記事を参照している状態で「いいね」をクリックする場合は、画面左側にアイコン一覧が縦に並んでいますので
のマークをクリックします。
良い記事が見つかったら
をクリックして作者を応援しましょう!📣
開発者の皆さん、こんにちは!
昨年初めて開催した日本の InterSystems 開発者コミュニティのコンテストですが、📣今年も開催します!📣
テーマは昨年と同じで InterSystems IRIS/InterSystems IRIS for Health に関連した内容であればどのような内容でもご応募いただけます。
🖋 InterSystems Japan 技術文書ライティングコンテスト2024:IRISに関連した記事 🖋
🎁 参加賞:投稿いただいた方全員に👚開発者コミュニティ特製Tシャツ👕をプレゼントいたします!
🏆 特別賞:選ばれた3作品に特別賞があります。
8/30更新:賞品情報追加しました!ぜひご確認ください!👇
開発者の皆様はじめまして。 私からはIRISのソースコントロール機能を用いたソースの自動チェック機能のご紹介をしたいと思います。 チーム開発では、ソースの可読性や実装方法等がある程度統一されるようにコーディング規約を作成すると思います。 しかし、メンバーの入れ替わりでコーディング規約の説明をしていても徹底されないことが起こることも少なくありません。 なので、ソースコントロールを使用してコンパイル時に自動的にチェックするようにしました。 IRIS内で完結させるメリットとして、エラーチェックだけでなくチェック後にエラーがなければコンパイルまで自動で行えること、 %Dictionary.ClassDefinition(クラス定義)を使用できるので、チェッククラスを作成しやすいこと等があげられます。
目次
1.ソースコントロールについて まず、ソースコントロールについて簡単に記載します。 ソースコントロールとは、一般的にコードに対する変更を追跡し管理することを表します。 IRISのソースコントロール機能には様々なメソッドが用意されています。 今回はそれを使用することでソースの自動チェック機能を実現していきます。 参考リンク:InterSystems IRIS とソース・コントロール・システムの統合、 ソース・コントロール設定の構成
2.今回用意したチェック用クラスの紹介
今回作成したチェック用クラスには基底クラスとして、「%Studio.SourceControl.Base」と「%Studio.Extension.Base」(%Studio.SourceControl.Baseの基底クラス)を使用しています。
上記のクラスにはログイン時のイベントやロード前イベントなどが定義されており、今回は「OnBeforeCompile」(コンパイル前イベント)を使用しました。

では、実際のチェック用クラスの内容をご紹介します。 今回は以下をコーディング規則として実装をしています。 ①クラスの命名チェック - 「XXXX」始まりのクラス名であること ②インデントチェック - インデントは4の倍数の半角空白で埋めること ③変数の利用チェック - 定義した変数は利用すること ④引数の利用チェック - パラメータとして受け取った引数は利用すること
Class User.CompileChk Extends %Studio.SourceControl.Base
{
/// 処理概要 :コンパイル前チェック
/// <br>IN :InternalName : コンパイル対象クラス
/// <br>OUT :%Status
/// <br>処理詳細:規約に則さない実装がされている場合、コンパイルエラーにする。
Method OnBeforeCompile(InternalName As %String, ByRef qstruct As %String) As %Status
{
Set InternalName = $REPLACE(InternalName, ".CLS", "")
Set clsDef = ##Class(%Dictionary.ClassDefinition).%OpenId(InternalName)
Set SKIPuFLG = $$$NO
Write !,"****************コンパイルチェック開始****************",!
Write "TARGET : "_InternalName,!
Set hasErr = 0
#; クラス名チェック
Set hasErr = hasErr + '##class(User.Chk.ClassNamingChecker).%New().IsCorrectDefine(clsDef)
#; インデントチェック
Set hasErr = hasErr + '##class(User.Chk.IndentChecker).%New().UseWrongIndent(clsDef)
#; 変数の利用チェック
Set hasErr = hasErr + '##class(User.Chk.UseValChecker).%New().UseVal(clsDef)
#; 引数の利用チェック
Set hasErr = hasErr + '##class(User.Chk.UseArgsChecker).%New().UseArgs(clsDef)
Write "****************コンパイルチェック完了****************",!
If (hasErr > 0) {
Return $$$ERROR($$$GeneralError, "コンパイルエラーがあります。")
} Else {
Return $$$OK
}
}
}
①クラスの命名チェッククラス - User.Chk.ClassNamingChecker Class User.Chk.ClassNamingChecker Extends %RegisteredObject {
/// 処理概要 :クラス定義の命名規約違反チェック
/// <br>IN :clsDef : クラス定義
/// <br>OUT :%Boolean
/// <br>処理詳細:クラスの命名がコーディング規約に従っているかどうかをチェックする。
Method IsCorrectDefine(clsDef As %Dictionary.ClassDefinition) As %Boolean
{
Write "*クラス名チェック",!
Set ret = $$$YES
If (clsDef '= "") {
Set clsName = clsDef.Name
Set ret = ..WriteStartWithStrErr(clsName)
}
Return ret
}
/// 処理概要 :命名先頭不正のエラー表示
/// <br>IN :clsName クラス名/ keyword キーワード
/// <br>OUT :%Boolean
/// <br>処理詳細:クラス名の先頭がキーワードで開始していなければエラーを表示する。
Method WriteStartWithStrErr(clsName As %String) As %Boolean [ Private ]
{
If ($FIND(clsName, ".XXXX") > 0) {
#; OK
Return $$$YES
} Else {
Write "E: クラス名はXXXXという単語で始まる必要があります。",!
Return $$$NO
}
}
}
クラス内で行っていること 引数として%Dictionary.ClassDefinition(クラス定義)を受け取り、クラス定義内のプロパティであるNameを使用することで クラス名を取得。取得したクラス名に対して、「.XXXX」を$FINDで検索することでクラスの先頭が「XXXX」であるかチェックを行います。
②インデントチェック - User.Chk.IndentChecker
Class User.Chk.IndentChecker Extends %RegisteredObject
{
/// 処理概要 :インデント不正チェック
/// <br>IN :clsDef : クラス定義
/// <br>OUT :%Boolean
/// <br>処理詳細:インデントが4の倍数になっているかをチェックをする。
Method UseWrongIndent(clsDef As %Dictionary.ClassDefinition) As %Boolean
{
Write "*インデント不正チェック",!
Set isCorrect = $$$YES
Set count = clsDef.Methods.Count()
For i = 1: 1: count {
Set cnt = 0
Set method = clsDef.Methods.GetAt(i)
Do method.Implementation.Rewind()
While ('method.Implementation.AtEnd) {
Set cnt = cnt + 1
Set line = method.Implementation.ReadLine()
If (line = "") {
Continue
}
If ('$MATCH(line, "^( {4}){1,}[^ ].*")) {
Set isCorrect = $$$NO
Write "E: インデントが4の倍数になっていません。: "_method.Name_"+"_cnt_line,!
}
}
}
Return isCorrect
}
}
クラス内で行っていること 引数として%Dictionary.ClassDefinition(クラス定義)を受け取り、クラス定義内のプロパティであるMethods.Count()でメソッドの数を取得。 メソッドごとに1行ずつチェックを行います。チェックは正規表現を使用(^( {4}){1,}[^ ].*の部分)し、半角スペースが4の倍数になっているかチェックを行います。
③変数の利用チェック - User.Chk.UseValChecker
Class User.Chk.UseValChecker Extends %RegisteredObject
{
/// 処理概要 :変数の利用チェック
/// <br>IN :clsDef : クラス定義
/// <br>OUT :%Boolean
/// <br>処理詳細:定義された変数が利用されているかをチェックをする。
Method UseVal(clsDef As %Dictionary.ClassDefinition) As %Boolean
{
Set isCorrect = $$$YES
Set count = clsDef.Methods.Count()
For i = 1: 1: count {
Set cnt = 0
Set method = clsDef.Methods.GetAt(i)
Do method.Implementation.Rewind()
Set args = method.FormalSpec
Set argList = {}
For j = 1: 1: $LENGTH(args, ",") {
Set arg = $REPLACE($REPLACE($REPLACE($PIECE($PIECE(args, ",", j), ":", 1), "&", ""), "*", ""), "...", "")
If (arg = "") {
Continue
}
Do argList.%Set(arg, "")
}
Set valList = {}
While ('method.Implementation.AtEnd) {
Set cnt = cnt + 1
Set line = method.Implementation.ReadLine()
If (line = "") {
Continue
}
If (..HasComment(line)) {
Continue
}
If ($FIND(line, "Set ") > 0) {
Set valNm = $PIECE($REPLACE($PIECE($PIECE(line, "Set ", 2), "="), " ", ""), "(")
#; オブジェクトへの参照は対象外。
If (($FIND(valNm, ".") = 0) && ($FIND(valNm, "$") = 0)) {
#; 変数の定義があればObjectに登録。
If (('valList.%IsDefined(valNm)) && 'argList.%IsDefined(valNm)) {
Do valList.%Set(valNm, $$$NO)
}
}
}
Set iter = valList.%GetIterator()
While iter.%GetNext(.key, .value) {
If ($FIND($REPLACE(line, "Set "_key_" ", ""), key) > 0) {
Do valList.%Set(key, $$$YES)
}
}
}
Set iter = valList.%GetIterator()
While iter.%GetNext(.key, .value) {
If ('value) {
Write "E: "_method.Name_"() にて変数"_key_"は定義されていますが、利用されていない可能性があります。",!
Set isCorrect = $$$NO
}
}
}
Return isCorrect
}
Method HasComment(line As %String) As %Boolean [ Private ]
{
Return ($MATCH(line, "^( )*#;.*") > 0) || ($MATCH(line, "^( )*//.*") > 0)
}
}
クラス内で行っていること ②で行ったようにメソッド単位でチェックを行います。method.FormalSpec でメソッドの引数のリストを含む文字列を取得します。 上記で取得した文字列から、引数のみを抽出して引数リストを作ります。ここまで来たら、メソッドを1行ずつチェックしていきます。 HasCommentメソッドでコメントの場合は読み飛ばすようにしています。まず、「Set」の使用をチェックします。(If ($FIND(line, "Set ") > 0) {) 使用されている場合でもオブジェクトへの参照は対象外とするため、「.」や「$」が使用されている場合は読み飛ばします。(If (($FIND(valNm, ".") = 0) && ($FIND(valNm, "$") = 0)) {) 「.」や「$」が使用されていないかつ、引数のリストに存在しない場合は後のチェックのためにリストに追加します。(value側は$$$NOで登録しておきます) チェックリストを順番にリストのキー項目が定義箇所以外で使用されているかチェックしていきます。(L50~L54) 使用されている場合、該当キー項目のvalueを$$$YESに更新しておきます。 チェック処理としては上記で完了です。あとはvalueが$$$NOの項目を洗い出して、チェック完了となります。
④引数の利用チェック - User.Chk.UseArgsChecker
Class User.Chk.UseArgsChecker Extends %RegisteredObject
{
/// 処理概要 :引数の利用チェック
/// <br>IN :clsDef : クラス定義
/// <br>OUT :%Boolean
/// <br>処理詳細:引数が利用されているかをチェックする。
Method UseArgs(clsDef As %Dictionary.ClassDefinition) As %Boolean
{
Write "*引数の利用チェック",!
Set ngKeyword = $LISTBUILD(")", """", "}")
Set isCorrect = $$$YES
Set count = clsDef.Methods.Count()
For i = 1: 1: count {
Set method = clsDef.Methods.GetAt(i)
Set args = method.FormalSpec
Do method.Implementation.Rewind()
Set str = method.Implementation.Read()
For j = 1: 1: $LENGTH(args, ",") {
Set arg = $REPLACE($REPLACE($REPLACE($PIECE($PIECE(args, ",", j), ":", 1), "&", ""), "*", ""), "...", "")
If (arg = "") {
Continue
}
Set isIgnore = $$$NO
Set ptr = 0
While $LISTNEXT(ngKeyword, ptr, value) {
#; NGリストの内容が含まれていると、切り出し対象外。
If ($FIND(arg, value) > 0) {
Set isIgnore = $$$YES
Quit
}
}
If (isIgnore) {
Continue
}
If ($FIND(str, arg) = 0) {
Write "E: 引数が利用されていません。: "_method.Name_"/"_arg,!
Set isCorrect = $$$NO
}
}
}
Return isCorrect
}
}
クラス内で行っていること まず、コメントなどを引っ掛けないためにチェックしない文字を定義します。(L10) ③と同様にメソッド単位でチェックをするようにします。(L13~) メソッドの引数を取得します。(L16) メソッドの最初の行からチェックするようにポインタをストリームの先頭にしてメソッドの読込を行います。(L18、L19) 引数の数分チェックをまわしていきます。(L20) 引数の中にチェックしない文字が入っているかチェックします。(L28~L34) チェックしない文字が入っていない場合は、読み込んだメソッドの中で引数が使用されているかチェックを行います。(L39~L42)
3.ソースコントロールへの設定
管理ポータルにて、[システム管理]⇒[構成]⇒[追加の設定]⇒[ソースコントロール] を開くと
ソースコントロールの設定画面となります。


ソースコントロールクラス名の一覧には「%Studio.SourceControl.Base」クラスを継承したクラスが表示されます。 ソースコントロールを行いたいネームスペースを選択し、使用したいソースコントロールクラスを選択⇒保存します。 今回はUSERのネームスペースにチェック用クラス用意していますが、%SYSにソースコントロールクラスを作成することで全てのネームスペースに対して使用することができます。
4.実際の動作 今回テスト用に用意したクラスが以下のクラスです。
Class User.Test.NewClass1 Extends %RegisteredObject
{
/// 処理概要 :テストメソッド
/// <br>IN :Str1 : テスト用文字列1
/// <br>IN :Str2 : テスト用文字列2
/// <br>OUT :%Boolean
Method TestMethod(Str1 As %String, Str2 As %String) As %Boolean
{
Set test1 = Str1
Set test2 = "TEST"
Set ^TESTG(test1,"abc") = "hugehuge"
Return $$$OK
}
}
クラス名が「XXXX」始まりでないこと。 - クラスの命名チェック インデントが4の倍数個の半角スペースになっていないこと。(L10) - インデントチェック 定義した変数test2が使用されていないこと。 - 変数の使用チェック TestMethodの引数として用意したStr2が使用されていないこと。 - 引数の使用チェック
実際にコンパイルした結果がこちらです。

すべてチェックに引っかかっており、コンパイルもされないようになっています。 では、チェッククラスに指摘された部分を修正したものをコンパイルしてみます。
Class User.Test.XXXXNewClass1 Extends %RegisteredObject
{
/// 処理概要 :テストメソッド
/// <br>IN :Str1 : テスト用文字列1
/// <br>IN :Str2 : テスト用文字列2
/// <br>OUT :%Boolean
Method TestMethod(Str1 As %String, Str2 As %String) As %Boolean
{
Set test1 = Str1
Set test2 = "TEST"
Set ^TESTG(test1,test2) = Str2
Return $$$OK
}
}

見事にコンパイルが成功しました。
5.感想 今回参考例として4つのチェックを行いましたが、工夫や組み込み方次第では色々なチェックを組み込めると感じました。 Ex.) 変数がキャメルケースになっているか、利用してほしくないプロパティ等が使用されているか etc… また、他のライブラリでチェックツールは色々とあるかと思いますが、今回はIRISの中だけで完結させており、 チェックだけでなくエラーが出なかった時はコンパイルまで通るところがやはり良い部分に感じました。 今回使用したクラスはGithubにアップしておりますので、興味のある方はご確認いただければと思います。 追記)インデントチェッククラスをEmbedded Pythonで記載してみました。(Githubにもアップしております)
Class User.Chk.IndentCheckerP Extends %RegisteredObject
{
/// 処理概要 :インデント不正チェック
/// <br>IN :clsDef : クラス定義
/// <br>OUT :%Boolean
/// <br>処理詳細:インデントが4の倍数になっているかをチェックをする。
ClassMethod UseWrongIndentP(clsDef As %Dictionary.ClassDefinition) As %Boolean [ Language = python ]
{
import iris
import re
print("*インデント不正チェック\n")
isCorrect = 1
count = clsDef.Methods.Count()
for i in range(count):
cnt = 0
method = clsDef.Methods.GetAt(i + 1)
while not method.Implementation.AtEnd:
cnt += 1
line = method.Implementation.ReadLine()
if line == '':
continue
if len(re.compile("^( {4}){1,}[^ ].*").findall(line)) == 0:
isCorrect = 0
print("E: インデントが4の倍数になっていません。: " + method.Name + str(cnt) + str(line) + "\n")
return isCorrect
}
}
以上になります。ご一読いただき、ありがとうございました。
CI/CD シリーズの新しい章へようこそ。ここでは、InterSystems テクノロジーと GitLab を使用したソフトウェア開発の様々な可能なアプローチを取り上げています。
今回は、相互運用性についてご紹介しましょう。
アクティブな相互運用性プロダクションがある場合、2 つの個別のプロセスフローが存在します。メッセージを処理する稼動中のプロダクションと、コード、プロダクションの構成、およびシステムデフォルト設定を更新する CI/CD プロセスフローです。
明らかに、CI/CD プロセスは相互運用性に影響しますが、 本題は次にあります。
プロダクションのライフサイクルから見てみましょう。
まず初めに、プロダクションを起動できます。 ネームスペースあたり 1 つのプロダクションのみを同時に実行でき、一般に(作業内容や過程を十分に理解していない限り)、ネームスペースごとに実行するプロダクションは 1 つ限りです。 1 つのネームスペース内で 2 つ以上の異なるプロダクションを切り替えるのは推奨されません。 プロダクションを起動すると、そのプロダクションに定義されたすべての有効なビジネスホストが起動します。 一部のビジネスホストが起動に失敗してもプロダクションの起動には影響しません。
ヒント:
##class(Ens.Director).StartProduction("ProductionName") を呼び出してプロダクションを起動します。OnStart メソッドを実装して、プロダクションの起動時に(ビジネスホストジョブが起動する前に)任意のコードを実行します。プロダクションが起動した後、Ens.Director は実行中のプロダクションを継続的に監視します。 プロダクションの状態は 2 つあります。プロダクションクラスとシステムデフォルト設定に定義された target state と、ジョブが作成されたときに適用された設定で現在実行しているジョブの running state です。 希望する状態と現在の状態が同一である場合はすべて良好ですが、異なる場合にはプロダクションを更新する必要があります。 通常、この場合には、システム管理ポータルのプロダクション構成ページに赤い「更新」ボタンが表示されます。
プロダクションを更新すると、現在のプロダクションの状態をターゲットプロダクションの状態に一致させることになります。
プロダクションを更新するために ##class(Ens.Director).UpdateProduction(timeout=10, force=0) を実行すると、各ビジネスホストにおいて以下が行われます。
これを各ビジネスホストに対して実行した後、UpdateProduction は一連の変更をビルドします。
そしてその後で適用します。
このようにして、何も変更せずに設定を「更新」するとプロダクションが停止しません。
ヒント:
##class(Ens.Director).UpdateProduction(timeout=10, force=0) を呼び出してプロダクションを更新します。
システム管理ポータルのデフォルトの更新タイムアウトは 10 秒です。 メッセージの処理がそれ以上かかることが分かっている場合は、タイムアウト時間がより長い Ens.Director:UpdateProduction を呼び出します。UpdateProduction は古いコードで BH を更新することはありません。 これは安全を優先した動作ではありますが、根底にあるコードが変更した場合にすべての実行中の BH を自動的に更新する場合は、以下のように行います。
まず、以下のように読み込んでコンパイルします。
do $system.OBJ.LoadDir(dir, "", .err, 1, .load)
do $system.OBJ.CompileList(load, "curk", .errCompile, .listCompiled)
listCompiled には、u フラグにより、実際にコンパイルされたすべての項目が含まれます(読み込まれるセットを最小限に抑えるには、git diffs を使用します)。 この listCompiled を使用して、コンパイルされたすべてのクラスの $lb を取得します。
set classList = ""
set class = $o(listCompiled(""))
while class'="" {
set classList = classList _ $lb($p(class, ".", 1, *-1))
set class=$o(listCompiled(class))
}
その後で、再起動が必要な BH のリストを計算します。
SELECT %DLIST(Name) bhList
FROM Ens_Config.Item
WHERE 1=1
AND Enabled = 1
AND Production = :production
AND ClassName %INLIST :classList
最後に、bhList を取得した後に、影響のあるホストを停止して起動します。
for stop = 1, 0 {
for i=1:1:$ll(bhList) {
set host = $lg(bhList, i)
set sc = ##class(Ens.Director).TempStopConfigItem(host, stop, 0)
}
set sc = ##class(Ens.Director).UpdateProduction()
}
プロダクションは停止可能です。つまり、すべての美自演すホストジョブにシャットダウンのリクエストを送信します(アクティブなメッセージがある場合はその処理が完了してから安全にシャットダウンされます)。
ヒント:
##class(Ens.Director).StopProduction(timeout=10, force=0) を呼び出してプロダクションを停止します。
システム管理ポータルのデフォルトの停止タイムアウトは 120 秒です。 メッセージの処理がそれ以上かかることが分かっている場合は、タイムアウト時間がより長い Ens.Director:StopProduction を呼び出します。OnStop メソッドを実装して、プロダクションの停止時に任意のコードを実行します。ここで重要なのは、プロダクションはビジネスホストの合計であることです。
この流れで、ビジネスホストのライフサイクルに進みましょう。
ビジネスホストは(プールサイズの設定値に応じて)同一のビジネスホストジョブで構成されています。 ビジネスホストを起動すると、すべてのビジネスホストジョブが起動します。 これらは並列して起動します。
個別のビジネスホストジョブは以下のように起動します。
(4)が完了すると、ジョブは設定またはコードを変更できないため、新しい/同じコードと新しい/同じシステムデフォルト設定をインポートしても、現在実行中の相互運用性ジョブに影響しません。
ビジネスホストジョブを停止すると、以下のようになります。
OnMessage メソッド、BS の場合は OnProcessInput メソッド、BPL BP の場合は状態の S<int> メソッド、BP の場合は On*メソッドを終了します)。force=0 までに処理されていない場合、そのビジネスホストのプロダクションの更新は失敗します(SMP に赤い更新ボタンが表示されます)。timeout の前に処理されたforce=1 で処理されたビジネスホストの更新では、ビジネスホストの現在実行中のジョブを停止し、新しいジョブを起動します。
すべてのビジネスホストは、新しいバージョンのビジネスルール、ルーティングルール、および DTL が利用できるようになると直ちにそれらを使用し始めます。 この状況では、ビジネスホストの再起動は不要です。
とは言え、時にはプロダクションの更新には個別のビジネスホストの停止が必要です。
この状況を考えてみてください。 任意の基準に基づいてメッセージをビジネスプロセス A または B にルーティングする現在のルーティングルール X があるとします。 新しいコミットにおいて、以下を同時に追加します。
このシナリオでは、先にルールを読み込んでからプロダクションを更新することはできません。 新しくコンパイルされるルールは、InterSystems IRIS がまだコンパイルしていないかもしれないビジネスプロセス C に直ちにルーティングし始めるか、相互運用性がまだ使用できるように更新されていないためです。 この場合、ルーティングルールでビジネスホストを無効に子、コードを更新し、プロダクションを更新して、ビジネスホストをもう一度有効にする必要がります。
注意:
ビジネスホスト間の依存関係は重要です。 ビジネスプロセス A と B があり、A が B にメッセージを送信するとします。 新しいコミットにおいて、以下を同時に追加します。
このシナリオでは、プロセス B を先に更新してから A を更新する必要があります。 これは次のいずれかの方法で行えます。
新しいバージョンのプロセス A と B に古いバージョンとの互換性がない場合のより困難な状況では、ビジネスホストの停止が必要となります。
更新後に、ビジネスホストが古いメッセージを処理できないことが分かっている場合、ビジネスホストキューが更新前に空になっていることを確認する必要があります。 これを行うには、ビジネスホストにメッセージを送信するすべてのビジネスホストを無効化し、そのキューが空になるのを待ちます。
はじめに、BPL BP の仕組みから簡単に説明します。 BPL BP をコンパイルした後、2 つのクラスが完全な BPL クラス名と同じ名前でパッケージに作成されます。
Thread1 クラスはメソッド S1, S2, ... Sn を含み、BPL 内のアクティビティに対応します。Context クラスにはすべてのコンテキスト変数があり、BPL が実行する次の状態もあります(S5)・また BPL クラスは永続であり、現在処理中のリクエストを格納します。
BPL は Thread クラスで S メソッドを実行し、それにおうじて BPL クラステーブル、Context テーブル、および Thread1テーブルを更新することによって機能します。ここで、「処理中」の 1 つのメッセージは BPL テーブル内の 1 行になります。 リクエストが処理されると、BPL は BPL、Context、および Thread エントリを削除します。 BPL BP は非同期であるため、1 つの BPL ジョブは S 呼び出しの間に情報を保存して異なるリクエストを切り替えることで、同時に多数のリクエストを処理できます。
たとえば、BPL は 1 つのリクエストが sync アクティビティに到達するまで処理し、BO からの応答を待ちます。 現在のコンテキストを %NextState プロパティ(Thread1 クラス)をレスポンスアクティビティ S メソッドに設定してディスクに保存し、BO が応答するまで他のリクエストを処理します。 BO が応答したら、BPL はコンテキストをメモリに読み込んで、%NextState プロパティに保存された状態に対応するメソッドを実行します。
では、BPL を更新したらどうなるでしょうか? まず、少なくとも以下の 2 つの条件の 1 つを満たしていることをチェックする必要があります。
少なくとも 1 つの条件を満たしている場合は、問題ありません。 更新後 BPL が処理する更新前リクエストが存在しないか、状態が最後に追加される、つまり古いリクエストもそこに含まれることが可能です(更新前リクエストが更新後 BPL アクティビティと処理に互換していることが前提です)。
ただし、処理中のアクティブなリクエストが存在し、BPL が状態の順序を変更した場合はどうでしょうか? 理想的には、待機できるのであれば、BPL 呼び出し元を無効にしてキューが空になるまで待機します。 Context テーブルが空であることも確認します。 キューには未処理のリクエストのみが表示され、Context テーブルは処理中のリクエストを格納することを覚えておきましょう。つまり、非常にビジーな BPL のキューサイズが 0 となる場合があり、それは正常です。 その後、BPL を無効にし、更新を実行して、以前に無効にしたすべてのビジネスホストを有効にします。
それが可能でない場合は(通常、非常に時間のかかる BPL がある場合。リクエストの処理に約 1 週間かかった BPL の更新ケースや更新ウィンドウが短すぎたケースを覚えています)、BPL バージョン管理を使用します。
または、更新スクリプトを書くことができます。 この更新スクリプトでは、更新された BPL が古いリクエストを処理できるように、古い次の状態を新しい次の状態にマッピングして、Thread1 テーブルで実行します。 もちろん更新期間中は BPL を無効にしておく必要があります。
とは言え、これは非常にまれな状況であり、通常は行う必要はありませんが、その必要性が生じた場合にはこれを使用することができます。
相互運用性は、根底にあるコードが変更した後でプロダクションを最新状態にするために必要となるアクションを最小限に抑えるために、洗練されたアルゴリズムを実装します。 SDS の更新ごとに安全なタイムアウトで UpdateProduction を呼び出せます。 それぞれのコードの更新については更新ストラテジーを決定する必要があります。
git diffs を使ってコンパイルされるコードの量を最小限に抑えると、コンパイル時間の軽減に役立ちますが、コードをそのコード自体で「更新」して再コンパイルするか、同じ値で設定を「更新」するとプロダクションの更新はトリガーされないか不要となります。
ビジネスルール、ルーティングルール、および DTL を更新してコンパイルすると、プロダクションを更新せずにすぐにアクセス可能になります。
最後に、プロダクションの更新は安全な操作であり、通常、停止は必要ありません。
この記事の執筆において貴重な支援を提供してくださった @James MacKeith、@Dmitry Zasypkin、および @Regilo Regilio Guedes de Souza に感謝申し上げます。
生成AIを活用したアプリケーション開発は、Python、JavaScriptなどのメジャー言語による体験記事がよく見られます。一方、IRISのObjectScriptの開発に言及された記事は比較的少ないのが現状です。そこで、本記事では生成AIがObjectScriptの開発にどこまで活用できるのかを検証しました。
特にDevOpsのプロセスにおいて、生成AIは様々なシーンでの活用が期待できます。今回は開発工程に注目し、以下の観点から生成AIの有効性を調査しました。
本記事の検証は以下の環境で行いました。
開発環境
開発ツール IRISの開発にはStudioやVSCodeなどが利用可能ですが、今回は生成AIの活用に特化したエディタ「Cursor」を使用しました。
Cursorを選定した理由 Cursorは、生成AIによる支援機能に特化したコードエディタで、以下の特徴があります:
生成AIの支援:コードの自動生成や提案、バグの検出、修正提案を行います。また、外部のドキュメントや複数のソースを指定し、生成内容に反映させる簡易なRAG機能も搭載されています。
VSCodeとの互換性:VSCodeをフォークして作られており、VSCodeユーザーはスムーズに移行できます。拡張機能もそのまま利用可能です。
IRISではv2024よりStudioは廃止となり、VSCodeが標準ツールとなります。そこで、VSCodeと互換性があり、生成AIとの親和性が高いCursorを選定しました。
選択した生成AIモデル GPT-4を使用しましたが、Claudeでも検証を行ったところ、どのモデルも大差ない結果となりました。 利用しやすいモデルを選んで試してみてください。
今回は以下の内容を検証しました。
まず、ユーザーに名前を入力させ、挨拶を返す簡単なルーチンを生成します。CursorでCommand+Lを押してチャットウィンドウを開き、以下のプロンプトを入力します。
あなたはObjectScriptの開発者です。
{仕様}をもとにコードを作成してください。
#仕様
- 以下の処理を実行するRoutineを作成する。
- macファイル名は"TEST.mac"とする。
1. ユーザーに名前の入力を促します。
2. 入力された名前が空であればエラーメッセージを表示します。
3. 名前が入力された場合、その名前を使って挨拶メッセージを表示します。

生成されたルーチンをTEST.macにコピーし、ターミナルで実行して動作を確認します。

簡易なプログラムの生成が確認できたので、次はより複雑なアプリケーションを作成してみます。
アプリケーションの仕様 IRISのREST通信機能を利用したAPIを作成します。ユーザーからのリクエストに基づき、サーバー上のデータベースから該当する商品情報をJSON形式で返します。
テーブルの作成 まずは、テーブルを作成しましょう。DDLの作成をAIに依頼します。
InterSystemsのIRISで{テーブル}を作成するためのDDLを出力してください
#テーブル
##名前
- Sample.Product
##列
- JanCd VARCHAR(13)
- ProductName VARCHAR(100)
- Price INT
## プライマリキー
- JanCd

生成されたDDLをターミナルから実行し、テーブルが正常に作成されたことを確認します。

テストデータの作成 テストデータの作成もAIにアシストしてもらいましょう。

テストデータが正常に登録されたことを確認します。

APIの作成 準備が整ったので、APIクラスを生成します。以下のプロンプトを使ってコードを作成します。
あなたはObjectScriptの開発者です。
{仕様}をもとに"Api.Product"クラスを作成してください。
#仕様
- REST通信でJSON形式で商品情報を返す。
- apiのURLは/products/:jancd
- %CSP.RESTクラスを継承する。
- Jsonデータの生成は%DynamicObjectクラスを利用する。
- レスポンスコードは、%responseを利用する。
1. jancdが指定されなかった場合、404を返す。指定された場合、以降の処理を続行する。
2. "Sample.Product"テーブルをSQLで検索する。
- 取得項目: JanCd,ProductName,Price
- 検索条件: :jancdが指定された場合、テーブルをJanCdが一致するレコードを検索する。
3. 検索結果をJSON形式でREST通信結果として出力する。

生成されたコードを確認し、必要に応じて修正を加えます。クエリ結果の取得箇所で%Next()が実行されていなかったため、以下の修正を行いました。
Set tResult = tStatement.%Execute(jancd)
// -*-*-*-*- 処理を追加しました -*-*-*-*-
Do tResult.%Next()
If tResult.%SQLCODE = 100 {
curlコマンドを使用してAPIの動作を確認し、正常にJSONデータが返却されることを確認します。

最終的なソースは以下の通りです。今回は一部修正が必要でしたが、基本的な部分は生成AIが正しく対応していました。
Class Api.Product Extends %CSP.REST
{
/// URLマッピング
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/products/:jancd" Method="GET" Call="GetProductInfo"/>
</Routes>
}
/// 商品情報を取得するメソッド
ClassMethod GetProductInfo(jancd As %String) As %Status
{
// jancdが指定されていない場合、404を返す
If jancd = "" {
Set %response.Status = 404
Quit $$$OK
}
// "Sample.Product"テーブルをSQLで検索する
Set tStatement = ##class(%SQL.Statement).%New()
Set tSQL = "SELECT JanCd, ProductName, Price FROM Sample.Product WHERE JanCd = ?"
Set tStatus = tStatement.%Prepare(tSQL)
If $$$ISERR(tStatus) {
Set %response.Status = 500
Quit tStatus
}
Set tResult = tStatement.%Execute(jancd)
Do tResult.%Next()
// 検索結果がない場合、404を返す
If tResult.%SQLCODE = 100 {
Set %response.Status = 404
Quit $$$OK
}
// 検索結果をJSON形式で出力する
Set tProduct = ##class(%DynamicObject).%New()
Do tProduct.%Set("JanCd", tResult.%Get("JanCd"))
Do tProduct.%Set("ProductName", tResult.%Get("ProductName"))
Do tProduct.%Set("Price", tResult.%Get("Price"))
Write tProduct.%ToJSON(),!
Quit $$$OK
}
}
次に、生成したAPIに商品名を部分一致で検索するメソッドを追加します。Cursorの外部ファイル参照機能を使い、先ほど生成したクラスを参考にしながら新しいメソッドを作成します。プロンプトは以下の通りです。
@Api.Product.cls
以下の{仕様}で、"Api.Product"クラスにメソッドを追加してください。
#仕様
- GetProductInfo()の処理を参考にすること。
- apiのURLは/products/name/:productname
- Sample.ProductテーブルからAPIで指定された値が、ProductNameと部分一致するレコードを検索し、検索した結果をJson形式で返す。
- 検索結果は配列にして返す。

生成したコードは以下になります。(追加部分のみ抜粋)
Class Api.Product Extends %CSP.REST
{
// ... 既存のコード ...
/// URLマッピング
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
// ... 既存のルート ...
<Route Url="/products/name/:productname" Method="GET" Call="GetProductInfoByName"/>
</Routes>
}
// ... 既存のコード ...
/// 商品名で商品情報を取得するメソッド
ClassMethod GetProductInfoByName(productname As %String) As %Status
{
// productnameが指定されていない場合、404を返す
If productname = "" {
Set %response.Status = 404
Quit $$$OK
}
// "Sample.Product"テーブルをSQLで検索する
Set tStatement = ##class(%SQL.Statement).%New()
Set tSQL = "SELECT JanCd, ProductName, Price FROM Sample.Product WHERE ProductName LIKE ?"
Set tStatus = tStatement.%Prepare(tSQL)
If $$$ISERR(tStatus) {
Set %response.Status = 500
Quit tStatus
}
Set tResult = tStatement.%Execute("%" _ productname _ "%")
// 検索結果がない場合、404を返す
If tResult.%SQLCODE = 100 {
Set %response.Status = 404
Quit $$$OK
}
// 検索結果をJSON形式で出力する
Set tProducts = ##class(%DynamicArray).%New()
While tResult.%Next() {
Set tProduct = ##class(%DynamicObject).%New()
Do tProduct.%Set("JanCd", tResult.%Get("JanCd"))
Do tProduct.%Set("ProductName", tResult.%Get("ProductName"))
Do tProduct.%Set("Price", tResult.%Get("Price"))
Do tProducts.%Push(tProduct)
}
Write tProducts.%ToJSON(),!
Quit $$$OK
}
// ... 既存のコード ...
}
生成されたコードを確認し、curlコマンドで動作を確認します。正常に実行されました。外部ファイル参照機能の利用により、自動生成の精度が向上していることがうかがえます。

自動生成の精度向上 基本コマンドを用いたプログラムは高い精度を発揮しますが、クラスを利用した複雑な処理では精度が低下する印象を受けました。プロンプトの指示を工夫することで、精度を高めることができそうです。 また、Cursorの外部ドキュメント、ファイル参照機能には大きな可能性を感じました。この機能を使えば、既存のリソースや、AIが学習していないライブラリの活用が期待できます。
セキュリティとプライバシー Cursorはプライバシーモードを備えており、データをサーバーに保持しない設定が可能です。しかし、Cursorに限らず生成AIの業務利用には慎重な調査が必要です。
今回の検証を通じて、生成AIのコード生成能力が向上していることを実感しました。特に、テストデータやDDLの生成は、開発の効率を大幅に向上させる可能性があります。アジャイル開発で迅速なモックアップの作成が求められる場面では、生成AIの効果的な活用が期待できそうです。一方で、既存システムの小規模な改修には、効果が限定的であるという印象を受けました。
この記事を作成したきっかけは、ObjectScriptの初学者向け演習問題を生成AIで作成した際、その問題と解答の品質が非常に高かったことです。業務での活用も十分可能であると思い、今回の検証を行いました。生成AIは、工夫次第でさらなる幅広い活用が期待できるツールだと感じています。
約 4 年のギャップを経て、私の CI/CD シリーズが帰ってきました! 長年にわたり、InterSystems の数社のお客様と連携し、様々なユースケースに対応する CI/CD パイプラインを開発してきました。 この記事で紹介する情報が誰かのお役に立てられれば幸いです。
この連載記事では、InterSystems テクノロジーと GitLab を使用したソフトウェア開発の様々な可能なアプローチを取り上げています。
取り上げたいトピックは広範にありますが、今回は、コードを超えた内容についてお話ししましょう。構成とデータについてです。
前回はコードの昇格について話しました。ある意味ステートレスで、常に、(おそらく)空のインスタンスから完全なコードベースへと進めます。 ただし、時にはデータまたは状態を提供することも必要です。 データには様々なタイプがあります。
これらすべてのデータタイプについて、およびソース管理にコミットしてからデプロイする方法について説明します。
システム構成は多数のクラスに広がっていますが、InterSystems IRIS はほとんどを XML にエクスポートできます。 まず最初に、これは以下についての情報を含むSecurityパッケージです。
これらのすべてのクラスは Exists、Export、および Import メソッドを提供しているため、環境間で移動することが可能です。
いくつかの欠点:
また、Export/Import の代わりに %Installer または Merge CPF を使用してこれらのほとんどを作成できることも覚えておくとよいでしょう。 いずれのツールもネームスペースとデータベースの作成をサポートしています。 Merge CPF はグローバルバッファーサイズなどのシステム設定を調整できます。
%SYS.Task クラスはタスクを保管し ExportTasks および ImportTasks メソッドを提供します。 また、タスクを 1 つずつインポート/エクスポートするには、上記のユーティリティクラスも確認してください。 タスクをインポートする際に StartDate またはその他のスケジュール関連のプロパティが過去の日時である場合、インポートエラー(ERROR #7432: Start Date and Time must be after the current date and time)が発生する可能性があることに注意しましょう。 ソリューションとして、LastSchedule を 0 に設定すると、InterSystems IRIS は新たにインポートしたタスクを最も近い未来に実行するようにスケジュールを設定し直します。
相互運用性本番には以下が含まれます。
最初の 2 つは Ens.Config パッケージにあり、%Export と %Import メソッドを使用できます。 認証情報とルックアップテーブルは上記のユーティリティクラスを使ってエクスポートします。 最近のバージョンでは、ルックアップテーブルは $system.OBJ クラスでエクスポート/インポートできます。
システムデフォルト設定 - 環境固有の設定のデフォルトの相互運用性メカニズムです。
システムデフォルト設定の目的は、本番定義を環境間でコピーするプロセスを単純化することにあります。 いずれのプロダクションにおいても、いくつかの設定の値はプロダクションの設計の一部として決定されます。そのためこれらの設定は通常すべての環境で同一です。 一方でその他の設定は、環境に合わせて調整する必要があり、これらの設定にはファイルパス、ポート番号などが含まれます。
システムデフォルト設定は、InterSystems IRIS がインストールされている環境に固有の値のみを指定する必要があります。 それに反し、プロダクションの定義では、すべての環境で同一となる設定の値を指定する必要があります。
これらを本番環境で使用することを非常にお勧めします。 システムデフォルト設定を転送するには、%Export と %Import を使用してください。
アプリケーションもおそらく設定を使用します。 その場合、システムデフォルト設定を使用することをお勧めします。 これは相互運用性メカニズムではありますが、設定は %GetSetting(pProductionName, pItemName, pHostClassName, pTargetType, pSettingName, Output pValue)(ドキュメント)からアクセスできます。 必要でないデフォルト設定を設定するラッパーを書くことができます。例:
ClassMethod GetSetting(name, Output value) As %Boolean [Codemode=expression]
{
##class(Ens.Config.DefaultSettings).%GetSetting("myAppName", "default", "default", , name, .value)
}
ほかにもカテゴリが必要な場合は、pItemName や pHostClassName 引数を公開することもできます。 設定は主に、インポート、システム管理ポータルの使用、Ens.Config.DefaultSettings クラスのオブジェクトの作成、または ^Ens.Config.DefaultSettingsD グローバルの設定によって設定できます。
ここでの主なアドバイスは、設定を 1 か所にまとめておき(システムデフォルト設定またはカスタムソリューションのどちらでもかまいません)、アプリケーションは指定された API のみを使用して設定を取得するようにすることです。 この場合、アプリケーション自体は環境について認識せず、残っているのは環境固有の値を含む集中設定ストレージを提供することだけです。 これを行うには、リポジトリに環境ブランチ名と同じファイル名で設定ファイルを含む設定フォルダを作成します。 次に、CI/CD フェーズにおいて、$CI_COMMIT_BRANCH環境変数を使って正しいファイルを読み込みます。
DEV.xml
TEST.xml
PROD.xml
環境ごとに複数のファイルがある場合は、環境ブランチに因んだフォルダ名を使用してください。 InterSystems IRIS 内部から環境変数値を取得するには、$System.Util.GetEnviron("name") を使用します。
一部のデータ(参照テーブル、カタログなど)を使用できるようにするには、いくつかの方法があります。
xml.gz ファイルにエクスポートし、$system.OBJ メソッドが必要に応じて xml.gz ファイルを自動的にアーカイブ(アーカイブ解除)するようにすることで改善できます。 このアプローチの主なデメリットは、XML であってもほとんどが base64 でエンコードされるため、データを人間が読み取れないことです。どの形式を使用するかは、ユースケースによって異なります。 ここでは、ストレージ効率の順に形式をリストしていますが、データ量があまり多くない場合には考慮する点ではありません。
状態は、CI/CD デプロイパイプラインをさらに複雑にしますが、InterSystems IRIS ではそれを管理するためのツールを豊富に提供しています。
開発者のみなさん、こんにちは!
今年も開発者コミュニティミートアップを東京にて開催いたします!(10月1日:お申込みサイトオープンいたしました!)
日 時:2024年11月8日(金)13時半~19時
場 所:AP東京八重洲 13階
<JR各線をご利用の場合>「東京駅」八重洲中央口より徒歩6分
<東京メトロ銀座線をご利用の場合>「日本橋駅」徒歩約5分/「京橋駅」徒歩約4分
<都営浅草線をご利用の場合>「宝町駅」徒歩約4分
※ヤエチカ24番出口から出るとわかりやすいです。
参加費:無料
定 員:50名
※定員に達した際は調整をお願いすることもございます。予めご了承下さい。
申 込:(申込受付終了しました)
IRISはPythonの豊富なライブラリや既存のPythonプログラムをそのまま利用する事も、COS内でネイティブにコーディングする事も可能となりました。 しかし開発において、いくつかの問題点があります。
Pythonを使ったプロジェクトを構築していると、バージョンの問題にあたる時があります。 古いバージョンで開発していたところに、使いたいライブラリが対応していなかった等です。 しかし、IRISのEmbedded Pythonを利用する場合には、Pythonランタイムのバージョンに影響される為、プロジェクトで使用するバージョンは、プロジェクト単位はなく、IRISのバージョン単位で決まってしまいます。 また、現時点ではこのPythonランタイムをアップグレードする事はできません。
私は既に開発済みのPythonプログラムをそのまま活用したいと思い、外部Pythonファイルを読み込んで利用する方法を取りました。 IRISでは外部Pythonファイルを特定の場所に配置する事で、そのファイルをimportする事が出来ます。 デフォルトは{インストールパス}/lib/python です。 しかし、デフォルトではこの1ヶ所のディレクトリが対象となっていて、さらにサブフォルダは対象外となる為、プロジェクト単位などで管理する事も出来ません。
PythonPathの値を変更する事で、別のディレクトリを指定できます。 また、複数のディレクトリを指定する事も可能です。 Windowsであればカンマ区切り、linux等であればコロン区切りで複数指定が可能です。 但し、間にスペースを入れると認識されませんので注意して下さい。 また、複数ディレクトリに同じ名前のモジュールが存在すると、最初に読み込まれた物を利用するようです。(筆者実体験から) また、このPythonPathの値は、Pythonの対象ディレクトリを指定した場所だけに変更するのではなく、デフォルトのディレクトリに追加される事に注意して下さい。 PythonPathを書き換えてもデフォルトのディレクトリは読み込まれています。 もしデフォルトのディレクトリに同じ名前のPythonファイルがあると、そちらが先にimportされるようなので注意が必要です。
PythonPathの指定
ここに記載されていないが、デフォルトのディレクトリも対象となている。
複数指定する場合は、スペースを入れない
/opt/iris/python/common: /opt/iris/python/project --> コロンの後にスペースを入れると認識されない。
IRISは外部Pythonファイルやライブラリを読み込むと、プロセスが閉じるまで再読み込みをしません。 ですので、外部Pythonファイルを修正しても即時反映しません。 この場合、プロセスを一度閉じて再実行する必要があります。 ターミナル実行時などであれば、対処法が思いつきますが、Webブラウザからの場合は、セッションが閉じられても再読み込みされません。 Webブラウザからのアクセスの場合は、ウェブゲートウェイを閉じる必要があります。それは結構な手間です。 そこで、即時反映に近い動きにする為には、importしたモジュールをリロードする方法があります。
importlib.reload({モジュール名})
これをCOSの中でモジュールをimportしている箇所に記載する事で、実行する毎にモジュールのリロードを行います。
import importlib
import module1
import module2
importlib.reload(module1)
importlib.reload(module2)
以上が、筆者が最近経験したEmbedded Pythonを利用する時に起こった問題点です。
InterSystems IRIS の新バージョンに、 Hierarchical Navigable Small World (HNSW) インデックス・アルゴリズムに基づく新しい近似最近傍探索 (ANN) インデックスが搭載されました。こちらは、ベクトル検索 早期アクセスプログラム で入手いただけます。これにより、大規模なベクトルデータセットに対して非常に効率の良い近似最近傍探索が可能となり、クエリパフォーマンスとスケーラビリティが大幅に向上しました。
HNSW アルゴリズムは、グラフベース構造を利用して高次元データのベクトル検索を最適化するよう設計されており、大規模なベクトル集合における近似近傍探索を高速化します。HNSW によって、レコメンデーションシステム、自然言語処理、その他の機会学習アプリケーションなどすべてにおいて検索時間が大幅に短縮します。
HNSWの主な利点:
• データセットサイズ増加後も、より高速な検索が可能
• 高精度をたもちながら、メモリ使用量を削減
• 既存の IRIS ベクトル検索とのシームレスな統合
開発者の皆さん、こんにちは。
2024年4月26日より、新しいプラットフォームへの移行に伴い、グローバル・マスターズ・プログラムへのアクセスを一時的に停止しておりましたが、
🚀 本日(EST時間)より Global Masters プログラム 再開です!

開発者の皆さん、こんにちは!
USコミュニティで現在開催しているいつもとちょっと違ったコーディングではないイベント:🚶♀️InterSystems ウォーキング・チャレンジ🚶♂️をご案内します!
(通勤通学でよく歩いている方、賞品Getのチャンスです!)
InterSystemsのウォーキング・チャレンジは、あなたの心を充電し、フィットネスを高めるのに役立ちます。 リューベックからリューネブルクまで、何世紀も前にヨーロッパを結んだ伝説の交易路「塩の道」を歩くバーチャルな旅に出かけましょう。
そして、トレッドミル、スマートウォッチ、メダルなどのエキサイティングな賞品をゲットしましょう!
👟🚶🧑🦼Lace Up, Step Out, and Code Better! 🔋💻💪
📅 期間:2024年9月23日~10月20日 11月8日まで(11月22日18時CETにこのチャレンジは終了します)
参加されたい方、以下詳細をご参照ください。
開発者の皆さん、こんにちは! InterSystems IRIS(以下、IRIS)を使用したアプリケーション開発において、皆さんは環境設定をどうされていますか? 私は最近になって、「インストールマニフェスト」という機能があることを知りました。 これは、管理ポータルでポチポチしていた作業をコード化・自動化できる強力なツールです! 最初こそとっつきづらかったものの良いところがたくさんあるなと思ったので、簡単にではありますが皆さんにその良さと始め方をご紹介したいと思います。
なお、私が使用しているIRISバージョンは以下です。
2022.1
バージョンが異なる場合、違う書き方になっているもの等が存在する場合がありますので、 公式ドキュメント等を参照し適宜読み替えていただければと思います。
IRISを使用するにあたって、管理ポータルでの環境設定は切っても切れない作業だと思います。 IRIS のインストールマニフェストを使用することで、ネームスペース、データベース、セキュリティ設定、InterOperabilityの有効化、 さらにはカスタムコードの実行まで、一連のプロセスを自動化することができます。
本記事では、実際のマニフェストファイルを例に挙げながら、IRIS の環境設定自動化の方法について解説します。
なお、本記事で解説しているサンプルコードの全文と、付随するフォルダ・ファイルについては、Githubにて公開しています。
IRIS のインストールマニフェストとは、%Installerというツールのことを指します。
インストールマニフェスト定義を記述するだけで、
IRIS環境設定を変更するために必要なコードを自動生成することができます。
他人に共有する際は、定義したインストールマニフェストを配布するだけで済みます。
インストールマニフェストは XML 形式で記述されます。
主要な要素として <Manifest>、<Namespace>、<Configuration>、<Database> などがあります。
なお、公式ドキュメントは以下です。
私が使ってみて感じた、インストールマニフェストのメリット・デメリットについてお伝えします。 全ての場合でインストールマニフェストが優れているとは言えないと思いますので、下記を参考に使用したほうが良いかどうか判断してもらえたらと思います。
メリット
デメリット
インストールマニフェストは、簡単に作成することができます。 例えばあなたがスタジオを使っている場合、下記の手順でテンプレートを作成できます。
ファイル > 新規作成 > 一般 > %Installer マニフェスト > OK


こうして作成されるマニフェストは、以下のようになっています。
Include %occInclude
/// %Installer Manifest MyApp.MyInstaller
Class MyApp.MyInstaller
{
/// マニフェスト定義.
XData MyManifest [ XMLNamespace = INSTALLER ]
{
<Manifest>
<Namespace>
<Configuration>
<Database>
<!-- Your Manifest code here -->
</Database>
</Configuration>
</Namespace>
</Manifest>
}
/// これは XGL により生成されたメソッド・ジェネレーターです。.
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 3, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
{
#; XGL ドキュメントでこのメソッドのコードを生成する.
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "MyManifest")
}
}
なにやら見覚えのないコードがたくさん生成されますが、安心してください。 そのうち、私たちが編集すればよいのは基本的に下記の部分のみです。
<Manifest>
<Namespace>
<Configuration>
<Database>
<!-- Your Manifest code here -->
</Database>
</Configuration>
</Namespace>
</Manifest>
詳細については後ほど解説しますが、ここでは基本的な構造について紹介していきます。
<Manifest>:すべてのタグのルートタグである必要がある。 他のすべてのタグを含む。<Namespace>:ネームスペースを定義する。 【親タグ:<Manifest>】<Configuration>:<Namespace>内で構成タグの親タグとして記述する必要がある。【親タグ:<Namespace>】<Database>:データベースを定義する。 【親タグ:<Configuration>】ここで覚えるべきは、<Manifest>タグは唯一無二ですべてのルートタグとなること、
その中身となるそれぞれのタグにも親子関係があるためそれを守らなければならないこと、の2点です。
これら以外の記述は、インストールマニフェストの作成や実行に必要なコードです。 詳細の説明については、下記のページにあるので任せたいと思います。
このマニフェストを実行したいときは、ターミナルで以下のコマンドを実行してください。 ※マニフェストを実行する時は、%SYSネームスペースで実行することを推奨します。(それ以外のネームスペースでも動作はします)
USER>zn "%SYS"
%SYS>do ##class(MyApp.MyInstaller).setup()
そうすると、マニフェストが環境設定のためのコードを生成し、そのコードが実際にIRISの環境設定を変更していきます。
それではさっそく、インストールマニフェストで実際にできることについてみていきましょう。 私が触ったことのある機能を中心に紹介していますが、前述の公式ドキュメントにはそれ以外の設定もできるXMLタグが多数紹介されていますので、参照してみてください。
マニフェストでは、変数を設定することができます。 変数設定ができるタグには2種類あります。
<Default>:変数値がまだ設定されていない場合のみ、変数値を設定します。(すでに値が設定されている場合、無効になる)【親タグ:<Manifest>】<Var>:マニフェストで使用できる変数を定義および設定します。【親タグ:<Manifest>】<Var Name="var1" Value="aaa" />
// 事前に変数 var1 が設定されているので、var1 は aaa のまま
<Default Name="var1" Value="bbb" />
// 事前に変数 var2 は設定されていないので、var2 は ccc になる
<Default Name="var2" Value="ccc" />
マニフェストを使用して、ネームスペースとそれに関連するデータベースを簡単に作成できます。 関連するタグは3種類です。
<Namespace>:ネームスペースを定義する。【親タグ:<Manifest>】
<Configuration>:<Namespace> 内で構成タグの親タグとして記述する必要がある。【親タグ:<Namespace>】<Database>:データベースを定義する。【親タグ:<Configuration>】
<Default Name="Namespace" Value="TESTNMSP"/>
<Default Name="DATADB" Value="${Namespace}-GBL"/>
<Default Name="CODEDB" Value="${Namespace}-RTN"/>
<Default Name="SetupDir" Value="C:\work\git"/>
<!-- ネームスペース作成 -->
<Namespace Name="${Namespace}" Code="${CODEDB}" Data="${DATADB}" Create="yes" Ensemble="0">
<!-- DB作成 -->
<Configuration>
<Database Name="${DATADB}" Dir="C:\IRISDB\${Namespace}\GBL" Create="yes" InitialSize="100" Resource="%DB_${DATADB}" PublicPermissions="R"/>
<Database Name="${CODEDB}" Dir="C:\IRISDB\${Namespace}\RTN" Create="yes" InitialSize="10" Resource="%DB_${CODEDB}" PublicPermissions="R"/>
</Configuration>
</Namespace>
(ネームスペース)

(データベース)

マニフェストを使用して、特定のネームスペースに既存のデータやコードをインポートすることもできます。 関連するタグは1種類です。(既出除く)
<Import>:ファイルをインポートする。(%SYSTEM.OBJ.ImportDir または %SYSTEM.OBJ.Load を使用する)【親タグ:<Namespace>】
<Default Name="Namespace" Value="TESTNMSP"/>
<Default Name="DATADB" Value="${Namespace}-GBL"/>
<Default Name="CODEDB" Value="${Namespace}-RTN"/>
<Default Name="SetupDir" Value="C:\work\git"/>
<Namespace Name="${Namespace}" Code="${CODEDB}" Data="${DATADB}" Create="yes" Ensemble="0">
<!-- グローバル、クラス、ルーチンインポート -->
<Import File="${SetupDir}\Test1" Flags="ck" Recurse="1" IgnoreErrors="1"/>
<Import File="${SetupDir}\Test2" Flags="ck" Recurse="1" IgnoreErrors="1"/>
</Namespace>


マニフェストを使用して、グローバルやクラスをマッピングすることもできます。 関連するタグは3種類です。(既出除く)
<GlobalMapping>:グローバルを現在のネームスペースにマッピングする。【親タグ:<Configuration>】
<ClassMapping>:パッケージを現在のネームスペースにマッピングする。【親タグ:<Configuration>】
<RoutineMapping>:ルーチンを現在のネームスペースにマッピングする。【親タグ:<Configuration>】
<Default Name="Namespace" Value="TESTNMSP"/>
<Default Name="Namespace2" Value="${Namespace}2"/>
<Default Name="DATADB2" Value="${Namespace2}-GBL"/>
<Default Name="CODEDB2" Value="${Namespace2}-RTN"/>
<Namespace Name="${Namespace2}" Code="${CODEDB2}" Data="${DATADB2}" Create="yes" Ensemble="0">
<Configuration>
<!-- DB作成 -->
<Database Name="${DATADB2}" Dir="C:\IRISDB\${Namespace2}\GBL" Create="yes" InitialSize="100" Resource="%DB_${DATADB2}" PublicPermissions="R"/>
<Database Name="${CODEDB2}" Dir="C:\IRISDB\${Namespace2}\RTN" Create="yes" InitialSize="10" Resource="%DB_${CODEDB2}" PublicPermissions="R"/>
<!-- グローバル、クラス、ルーチンマッピング設定 -->
<GlobalMapping Global="test2" From="${DATADB}"/>
<ClassMapping Package="Test2" From="${CODEDB}"/>
</Configuration>
</Namespace>


マニフェストを使用して、ロールやユーザアカウントを複数作成することができます。 関連するタグは2種類です。
<Role>:ロールを定義する。【親タグ:<Manifest>】
<User>:ユーザを定義する。【親タグ:<Manifest>】
<Default Name="TestUserPw" Value="12345"/>
<!-- ロール作成・変更 -->
<Role
Name="TestOperator"
Description="テスト運用者ロール"
Resources="%DB_TESTNMSP-GBL:RW,%DB_TESTNMSP-RTN:RW"
RolesGranted="%Developer"/>
<Role
Name="TestAdministrator"
Description="テスト管理者ロール"
RolesGranted="%All"/>
<!-- ユーザ作成・変更 -->
<User
Username="TestUser1"
PasswordVar="TestUserPw"
Fullname="テストユーザ1"
Roles="TestOperator"
Namespace="USER"
Enabled="1"
Comment="テストユーザ1"/>
<User
Username="TestUser2"
PasswordVar="TestUserPw"
Fullname="テストユーザ2"
Roles="TestAdministrator"
Namespace="USER"
Enabled="1"
Comment="テストユーザ2"/>
(ロール)



(ユーザ)


また、管理ポータルのセキュリティ設定をまるっと読み込むこともできます。 これにより、管理ポータルやターミナル等のログインの有効化や、詳細のセキュリティ設定までを一括で行うことができます。 ロールやユーザアカウントについても、この読み込みにより一括で作成することができます。 関連するタグは2種類です。(既出除く)
<Invoke>:クラス・メソッドを呼び出して、実行結果を変数の値として返す。【親タグ:<Namespace>】
<Arg>:<Invoke> または <Error> から呼び出されるメソッドに引数を渡す。【親タグ:<Invoke>, <Error>】
<Default Name="SetupDir" Value="C:\work\git"/>
<Namespace Name="%SYS" Create="overwrite">
<!-- セキュリティ設定のインポート -->
<Invoke Class="Security.System" Method="ImportAll" CheckStatus="false">
<Arg Value="${SetupDir}\SecurityExport.xml"/>
</Invoke>
</Namespace>
これらのタグは、セキュリティ設定のインポート以外にも様々な用途に使用することができます。 例えば、タスクスケジュール全体のインポートをセキュリティと同じように行ったり、自作のクラスの処理を実行したり、既存のシステムクラスの処理を実行したりなどです。
※管理ポータルのログインの有効化設定については、2024.3以降のバージョンであればマニフェストのタグにて変更することができます。
(ターミナルのログイン有効化については、現在は<Invoke>を用いる方法か、後述の別途マニフェスト以外のコードを書く方法で実現するしかありません)
具体的には、下記のタグで実現できます。
<CSPApplication>:クラス内で定義されている 1 つ以上の Web アプリケーションを定義する。【親タグ:<Namespace>】
※セキュリティ設定のインポートの事前準備として、管理ポータルのセキュリティ設定(上述の「SecurityExport.xml」にあたるもの)をXMLとしてエクスポートしておく必要があります。
エクスポートは、次のように実施します。
ターミナルで^SECURITY ユーティリティを起動し、12) System parameter setup > 5) Export All Security settings を選択します。
そうすると、C:\InterSystems\[IRISインスタンス名]\mgr\SecurityExport.xml ができているはずです。
(IRISを入れている場所によっては出力場所は異なります)
★4.セキュリティ情報(ユーザ・サービスなど)のインポートInterSystems 製品の設定内容をインポート/エクスポートする方法 > セキュリティについて
%SYS>Do ^SECURITY
:
Option? 12 // System parameter setup
:
Option? 5 // Export All Security settings
Export ALL security records? Yes => Yes
Export to file name SecurityExport.xml =>
Parameters? "WNS" =>
Confirm export of selected security records to SecurityExport.xml? No => Yes
(デフォルトでは管理ポータルのログイン時にパスワード認証はないが、それをマニフェストで有効化した例)

InterOperability機能を使用する場合、専用のネームスペースを作成することができます。
また、プロダクションを自動的に設定することもできます。
関連するタグは2種類です。<Namespace>も改めて紹介します。
<Namespace>:ネームスペースを定義する。【親タグ:<Manifest>】
<Production>:プロダクションを定義する。【親タグ:<Namespace>】
<Default Name="Namespace3" Value="ENSNMSP"/>
<Default Name="DATADB3" Value="${Namespace3}-GBL"/>
<Default Name="CODEDB3" Value="${Namespace3}-RTN"/>
<Default Name="SetupDir" Value="C:\work\git"/>
<Namespace Name="${Namespace3}" Code="${CODEDB3}" Data="${DATADB3}" Create="yes" Ensemble="1">
<!-- Database作成などの設定は省略 -->
<!-- グローバル、クラス、ルーチンインポート -->
<Import File="C:\work\git\Test3" Flags="ck" Recurse="1" IgnoreErrors="1"/>
<!-- プロダクションの作成 -->
<Production Name="Test3.job.Main" AutoStart="1" />
</Namespace>

マニフェストを作成していると、管理ポータルでは設定できるけどマニフェストではどうやるのだろう?という設定に度々出会います。 公式ドキュメントを見るなどしてマニフェストでの実現方法がわかる場合もありますが、 中にはマニフェストでは実現方法がないもの、後続のバージョンでは修正されたが今使っているバージョンではバグが残っており実現できないもの、 等がある場合があります。
その場合、あきらめて管理ポータルで設定するというのも手ですが、 別途コード化してマニフェストの処理と一緒に実施してしまうのもありです。
実際に、いくつかの設定をマニフェスト以外のコードで実現し、それをマニフェストと一緒に実行するコードの簡単な例をご紹介します。
下記のコードでは、マニフェストの生成・実行処理であるsetupメソッド(前述のスタジオでの新規作成にて自動作成されるメソッド)を、 自作のsetupExecuteというメソッドでラップしています。 その上で、ロケールの変更や不要タスクの一時停止といった処理をコードで記述し、実行します。
/// <h2>マニフェスト生成・実行処理</h2>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).setup()
/// </p></pre>
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 3, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
{
#; XGL ドキュメントでこのメソッドのコードを生成する.
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "MyManifest")
}
/// <h2>セットアップ開始処理</h2>
/// <p>return %Status</p>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).setupExecute()
/// </p></pre>
ClassMethod setupExecute(ByRef pVars) As %Status
{
Set tSC = 0
New $NAMESPACE
Set $NAMESPACE = "%SYS"
#; すでにネームスペースがあるときはManifestの処理はスキップ
If ('##class(%SYS.Namespace).Exists("TESTNMSP")) {
Set tSC = ..setup(.pVars)
}
#; 日本語ロケールへ変更
Do ##class(Config.NLS.Locales).Install("jpuw")
#; 不要なタスクを停止
Do ..SuspendTask()
Return tSC
}
/// <h2>タスク停止</h2>
/// <p>不要なタスクスケジュールを停止する。</p>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).SuspendTask()
/// </p></pre>
ClassMethod SuspendTask()
{
Set targetId = -1
Set query = 0
Set query($INCREMENT(query)) = "SELECT %ID,Name FROM %SYS.Task"
Set tStatement = ##class(%SQL.Statement).%New()
Do tStatement.%Prepare(.query)
Set result = tStatement.%Execute()
While (result.%Next()) {
If (result.%GetData(2) = "機能トラッカー") {
Set targetId = result.%GetData(1)
Quit
}
}
If (targetId '= -1) {
#; 機能トラッカーを一時停止
Do ##class("%SYS.TaskSuper").Suspend(targetId)
}
}
これを実行する時は、setupメソッドではなくsetupExecuteメソッドを、ターミナルから実行します。 %SYSネームスペースから実行してください。
USER>ZN "%SYS"
%SYS>Do ##class(MyApp.MyInstaller).setupExecute()
(機能トラッカーを一時停止した結果)

本題からは逸れてしまいますが、「InterSystems Embedded Python」について私は今まで触ったことがなかったため、この機会にチャレンジしてみました。 簡単にですが、その結果と所感について述べたいと思います。
6-1の内容を、Embedded Pythonを用いて書き換えたコードは以下です。 ※setupメソッドについては「objectgeneratorメソッド」という特別なメソッドのため、ObjectScriptのみでしか動作しません。そのため、Pythonへの書き換えはできないようでした。
/// <h2>マニフェスト生成・実行処理</h2>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).setup()
/// </p></pre>
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 3, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
{
#; XGL ドキュメントでこのメソッドのコードを生成する.
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "MyManifest")
}
/// <h2>セットアップ開始処理</h2>
/// <p>return %Status</p>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).setupExecute()
/// </p></pre>
ClassMethod setupExecute(pVars As %SYS.Python = "") As %Status [ Language = python ]
{
import iris
tSC = 0
# すでにネームスペースがあるときはManifestの処理はスキップ
if not iris.cls('%SYS.Namespace').Exists('TESTNMSP') == 1:
tSC = iris.cls(__name__).setup(pVars)
# 日本語ロケールへ変更
iris.cls('Config.NLS.Locales').Install('jpuw')
# 不要なタスクを停止
iris.cls(__name__).SuspendTask()
return tSC
}
/// <h2>タスク停止</h2>
/// <p>不要なタスクスケジュールを停止する。</p>
/// <p><pre>SAMPLES>
/// Do ##class(MyApp.MyInstaller).SuspendTask()
/// </p></pre>
ClassMethod SuspendTask() [ Language = python ]
{
import iris
targetId = -1
df = iris.sql.exec('SELECT %ID,Name FROM %SYS.Task').dataframe()
for i in range(len(df)):
if df.iloc[i, 1] == '機能トラッカー':
targetId = df.iloc[i, 0]
break
if not targetId == -1:
# 機能トラッカーを一時停止
iris.cls('%SYS.TaskSuper').Suspend(int(targetId))
}
※SuspendTaskメソッドの実行に当たっては、事前にpandasライブラリのインストールが必要です。 未インストールの場合、下記のコマンドを実行してインストールを実行してください。 (IRISを入れている場所によっては対象のパスは異なります)
> cd C:\InterSystems\[IRISインスタンス名]\bin
> irispip install --target ..\mgr\python pandas
Embededd Pythonを少しだけ使用してみた所感としては、 純粋にPythonを書くところは知っている記法をそのまま書けるので苦なく書けましたが、 IRISとの連携部分(IRISのクラスを利用するなどIRIS独自の機能を利用したいとき)は都度書き方を調べる必要があったので少し大変でした。 一度書き方を覚えてしまえばより多くのコードを書く時でも問題なさそうですが、慣れが必要だと思います。 ObjectScriptの記法に慣れていると、ObjectScriptで当たり前にできることがPythonでは実装が難しい、直感的に書けない場合もあるようなので、注意が必要です。
しかし、それを踏まえてもコードをPythonで書けるのは良いと思いました! Pythonの記法のメリットとしてObjectScriptよりもシンプルに記載できる箇所も多いですし、 前述のPythonでは実装が難しい部分についても、バージョンがあがっていくことで機能は徐々に追加されていくと思いますので、これからに期待ができます。 なにより、ObjectScriptの経験がない人に対しても共通の言語を持てるというのはとても素晴らしいことだと思います。
これを実行する時は、ObjectScriptのときと同じで問題ありません。 %SYSネームスペースから実行してください。 ※そうでないと、「Config.NLS.Locales」の実行時にエラーとなります(%SYSにあるパッケージのため)
USER>ZN "%SYS"
%SYS>Do ##class(MyApp.MyInstaller).setupExecute()
IRIS のインストールマニフェストを使用することで、環境設定の自動化が可能になり、共通の環境を素早く構築できます。 これにより、開発チームの生産性が向上し、人為的なミスも減らすことができそうです。
本記事で興味を持っていただいた方は、ぜひインストールマニフェストを使ってみてください! 以上、ここまでお読みいただきありがとうございました。
開発者の皆様、こんにちは。
USコミュニティで開催していましたInterSystemsデベロッパーツールコンテスト の勝者が発表されました!
今回は 17 applications 🔥の素晴らしいアプリケーションの投稿がありました。
%20(2)(2).jpg)