外部言語の操作
$system.external インタフェースでは、埋め込み言語コード (ObjectScript または組み込み Python) から外部言語共有ライブラリ・ファイル (.jar、.dll、.py) を使用できます。Gateway オブジェクトは、Java、.NET、または Python 環境で実行されているインターシステムズの外部サーバへの接続を提供します。共有ライブラリ・クラスのメソッドとプロパティを $system.external インタフェースから直接呼び出し、ObjectScript プロキシ・オブジェクトを生成して、対応する Java、.NET、または Python ターゲット・オブジェクトのすべてのメソッドとプロパティにアクセスできます。

-
ゲートウェイ・プロセスは、InterSystems IRIS ネームスペース内で実行され、ObjectScript アプリケーションの接続を管理します。各ゲートウェイは 1 つ以上の外部共有ライブラリに接続でき、ライブラリ内の使用可能なすべてのクラスへのアクセスを提供します。
-
InterSystems 外部サーバ・プロセスは、外部言語環境 (Java、.NET、または Python) で実行され、外部言語共有ライブラリへのインタフェースを提供します。個々のサーバ・スレッドは、クラスのメソッドとプロパティへのアクセスを提供し、プロキシとターゲット・オブジェクト間の通信を管理します。
-
双方向 TCP/IP 接続により、ゲートウェイ・オブジェクトと外部サーバはメッセージを交換できます。各外部サーバ・プロセスは、接続を一意に識別するポート番号を使用して、独自のゲートウェイ・プロセスとペアになっています。
以下のセクションでは、接続を確立し、ターゲットを定義し、プロキシ・オブジェクトを使用する方法を説明します。
-
ゲートウェイの作成とプロキシ・オブジェクトの使用 — 外部言語オブジェクトのプロキシを作成する方法を示します。
-
ターゲット・ソフトウェアへのパスの定義 — ターゲット・オブジェクトの作成に使用される Java、.NET、または Python クラスへのパスを指定する方法を示します。
-
オブジェクト・ゲートウェイ・コードのアップグレード — 古いオブジェクト・ゲートウェイ・テクノロジからのアップグレードに関するヒントを提供します。
ゲートウェイの作成とプロキシ・オブジェクトの使用
接続の開始、プロキシ・オブジェクトの作成、およびメソッドの呼び出しのプロセス全体を、1 つの文に圧縮できます。以下の例は Java 外部サーバを開始し、ターゲット・クラス java.util.Date のインスタンスのプロキシを作成し、日付を表示するメソッドを呼び出します。
write $system.external.getJavaGateway().new("java.util.Date").toString()
Sun May 02 15:18:15 EDT 2021
この 1 行のコードは、プロキシ・オブジェクトの作成と使用のプロセス全体を示します。Java ゲートウェイ接続を確立し、クラス java.util.Date のインスタンスと対応する ObjectScript プロキシを作成し、メソッド toString() から返された日付を書き込みます。
InterSystems ターミナルでこの呼び出しを実行してみましょう。C# と Python での同等のコマンドを以下に示します。
write $system.external.getDotNetGateway.new("System.DateTime",0).Now
write $system.external.getPythonGateway.new("datetime.datetime",1,1,1).now().strftime(%c)
外部サーバは InterSystems IRIS のインストール時に自動的に設定され、通常、それ以上注意を払わなくても動作します (そうでない場合、“外部サーバの定義のトラブルシューティング” を参照してください。希望の言語のプラットフォームを識別するパス設定を変更する必要がある場合があります)。
前の例では、すべてを 1 行に圧縮していましたが、その行は以下の 3 つの重要な動作を実行します。
-
まず、ObjectScript と外部サーバ間の接続をカプセル化する ObjectScript Gateway オブジェクトを作成します。ゲートウェイを永続変数に割り当てることができます。
set javaGate = $system.external.getJavaGateway()
-
次に、ゲートウェイ・オブジェクトの new() メソッドを呼び出します。これは java.util.Date のインスタンスを外部サーバ・スレッドに作成し、対応するプロキシ・オブジェクトを ObjectScript プロセスに作成します。プロキシ・オブジェクトを永続変数に割り当てることもできます。
set dateJava = javaGate.new("java.util.Date")
-
最後に、プロキシのオブジェクト・メソッドを呼び出します。ターゲット・オブジェクトは呼び出しをエコーし、結果をプロキシに返します。
write dateJava.toString()
以下の例は、サポートされているすべての言語でのこれらの手順を示しています。
Java、.NET、および Python 外部サーバそれぞれに、getJavaGateway()、getDotNetGateway()、getPythonGateway() の固有のゲートウェイ作成メソッドがあります。これらの各メソッドは既定の構成で外部サーバを開始し、Gateway オブジェクトを返して接続を管理します。
set javaGate = $system.external.getJavaGateway()
set netGate = $system.external.getDotNetGateway()
set pyGate = $system.external.getPythonGateway()
また、カスタマイズされた外部サーバ構成で使用するための汎用の getGateway() メソッドもありますが (詳細は “外部サーバの定義のカスタマイズ” を参照)、ほとんどの目的には既定のメソッドで十分なはずです。
各ゲートウェイ・オブジェクトには、ターゲット・オブジェクトとプロキシ・オブジェクトを作成するための new() メソッドがあります。new() メソッドへの各呼び出しでは、クラス名と必要なすべての引数を指定します。呼び出しを実行すると、外部サーバはクラスのターゲット・インスタンスを作成し、ゲートウェイはターゲットと同じメソッドとプロパティのセットを持つ対応するプロキシ・オブジェクトを作成します。プロキシへの各呼び出しはターゲットによってエコーされ、結果がプロキシに返されます。
この例では、3 つの異なる外部サーバ (各言語に独自のサーバが必要であるため) を介して接続される 3 つのプロキシ・オブジェクトを作成します。
set dateJava = javaGate.new("java.util.Date")
set dateNet = newGate.new("System.DateTime",0)
set datePy = pyGate.new("datetime.datetime",1,1,1).now()
各プロキシは、その他の ObjectScript オブジェクトと同様に扱うことができます。3 つすべてを、同じ ObjectScript アプリケーションで使用できます。
同じ文で、3 つすべてのプロキシ・オブジェクトからのメソッド呼び出しとプロパティ呼び出しを使用できます。
write !," Java: "_dateJava.toString(),!," .NET: "_dateNet.Now,!, "Python: "_datePy.strftime("%c")
Java: Sun May 02 15:18:15 EDT 2021
.NET: 2021-05-02 16:38:36.9512565
Python: Sun May 02 15:23:55 2021
Java および Python の例はメソッドの呼び出しで、.NET の例ではプロパティを使用しています。
これまでの例では、システム・クラスのみを使用していました (これらはいつでも利用可能なので、説明しやすいためです)。その他の場合は、使用する前にクラスの場所を指定する必要があります。詳細は、後述する “ターゲット・ソフトウェアへのパスの定義” を参照してください。
外部言語アプリケーションで ObjectScript オブジェクトを制御できるようにする、逆プロキシ・オブジェクトを作成することもできます。詳細は、以下のセクションを参照してください。
ターゲット・ソフトウェアへのパスの定義
すべての Gateway オブジェクトには、特定言語のソフトウェア・ライブラリへのパスのリストを格納できます。Jave ゲートウェイは .jar ファイルを受け入れ、.NET ゲートウェイは .dll アセンブリを受け入れ、Python ゲートウェイは .py (モジュールまたはクラス) ファイルを受け入れます。
addToPath() メソッドでは、リストへの新しいパスを追加できます。path 引数は 1 つのパスを含む単純な文字列にするか、複数のパスを含む動的配列にすることができます。以下に例を示します。
do javaGate.addToPath("/home/myhome/someclasses.jar")
do netGate.addToPath("C:\Dev\myApp\somedll.dll")
do pyGate.addToPath("/rootpath/person.py")
Java ゲートウェイと .NET ゲートウェイは、1 つ以上の共有ライブラリを含むフォルダへのパスも受け入れます。高度な Python オプションの詳細は、“Python パッケージとクラス・パスの指定” を参照してください。
%DynamicArrayOpens in a new tab クラスは、JSON 配列構造を簡単に作成できるようにする ObjectScript ラッパです。動的配列の %Push() メソッドを使用して、配列にパスの文字列を追加します ("JSON の使用" の "動的配列での %Push と %Pop の使用" を参照)。以下の例は、配列 pathlist に 2 つのパスを追加し、それを Java Gateway オブジェクトの addToPath() メソッドに渡します。
set pathlist = []
do pathlist.%Push("/home/myhome/firstpath.jar")
do pathlist.%Push("/home/myhome/another.jar")
do javaGate.addToPath(pathlist)
path 引数は、複数のパス文字列を含む %Library.ListOfDataTypesOpens in a new tab のインスタンスとしても指定できますが、使いやすさを考慮すると動的配列をお勧めします ("JSON の使用" の "動的配列での %Push と %Pop の使用" を参照)。
以下の例は、各言語にクラスを指定する方法を示しています。
Java の場合、パスにはフォルダまたは jar を指定できます。以下の例は、someclasses.jar へのパスを追加し、次に someclasses.classname のインスタンスのプロキシを作成します。
set javaGate = getJavaGateway()
do javaGate.addToPath("/home/myhome/someclasses.jar")
set someProxy = javaGate.new("someclasses.classname")
.NET の場合、パスにはフォルダまたはアセンブリを指定できます。以下の例は、someassembly.dll へのパスを追加し、次に someassembly.classname のインスタンスのプロキシを作成します。
set netGate = getDotNetGateway()
do netGate.addToPath("C:\Dev\myApp\somedll.dll")
set someProxy = netGate.new("someassembly.classname")
Python の場合、パスにはモジュールまたはパッケージを指定できます。モジュールがパッケージの一部である場合、モジュールのパスはパッケージの最上位のディレクトリから開始するドット表記で指定する必要があります。例えば、モジュール Foo.py へのパスは、以下の 2 つの方法のどちらかで指定できます。
-
モジュールとして処理される場合の標準のパス表記 : C:\Dev\demo\Foo.py
-
パッケージ demo の一部として処理される場合のドット表記 : C:\Dev\demo.Foo.py
以下の例は、動的配列を使用して、フォルダ C:\Dev\demo\ に含まれる 2 つの異なるファイルへのパスを追加します。ファイル Foo.py には、パッケージ化されていないクラス personOne が含まれ、ファイル Bar.py には、パッケージ demo の一部であるクラス personTwo が含まれます。new() を呼び出すと、以下のように両方のクラスのプロキシが作成されます。
set pyPaths = []
do pyPaths.%Push("C:\Dev\demo\Foo.py")
do pyPaths.%Push("C:\Dev\demo.Bar.py")
set pyGate = getPythonGateway()
do pyGate.addToPath(pyPaths)
set fooProxy = pyGate.new("Foo.personOne")
set barProxy = pyGate.new("demo.Bar.personTwo")
クラスのないモジュールまたはパッケージのターゲットを割り当てることも可能です ("Python パッケージとクラス・パスの指定" を参照)。
Python パッケージとクラス・パスの指定
addToPath() メソッドでは、Python モジュールまたはクラスへのパスのリストを作成できます (概要は、"ターゲット・ソフトウェアへのパスの定義" を参照)。Python のターゲットを指定する構文は、ターゲットがパッケージまたはクラスのいずれに含まれるか、その両方か、またはどちらでもないかによって異なります。以下の例は、パスが addToPath() で指定され、クラス名が new() で指定される場合を示しています。
クラスの例では、person がメイン・クラスで、company はそれがインポートするクラスです。インポートされるクラスは、それが別個のファイル内にあるとしても、指定する必要はありません。
クラスのないモジュールまたはパッケージのターゲットも割り当てられますが、それらは実質的に静的なメソッドおよびプロパティに制限されます (クラスなしでクラス・インスタンス・メソッドを持つことはできないため)。
パッケージもクラスもないモジュールは、ファイル名を使用します。new() の引数は、ファイル名の後にピリオドが続き、クラスがないことを示していることに注意してください。
do pyGate.addToPath("/rootpath/noclass.py")
set proxy = pyGate.new("noclass."))
メイン・クラス person およびインポートされるクラス company は両方ともファイル /rootpath/onefile.py 内にあります。
do pyGate.addToPath(/rootpath/onefile.py")
set proxy = pyGate.new("onefile.person")
メイン・クラス person およびインポートされるクラス company は、/rootpath 内の別個のファイルに含まれます。
do pyGate.addToPath(/rootpath/person.py")
set proxy = pyGate.new("person.person")
クラスなしのパッケージでは、パッケージ名とファイル名を使用します。この例でのファイルの実際のパスは /rootpath/demo/noclass.py になります。new() の引数はピリオドで終了し、クラスがないことを示しています。
do pyGate.addToPath(/rootpath/demo.noclass.py")
set proxy = pyGate.new("demo.noclass.")
メイン・クラス person およびインポートされるクラス company は両方ともファイル /rootpath/demo/onefile.py 内にあります:
do pyGate.addToPath(/rootpath/demo.onefile.py")
set proxy = pyGate.new("demo.onefile.person")
メイン・クラス person およびインポートされるクラス company は、/rootpath/demo 内の別個のファイルに含まれます。
do pyGate.addToPath(/rootpath/demo.person.py")
set proxy = pyGate.new("demo.person.person")
オブジェクト・ゲートウェイ・コードのアップグレード
外部サーバでは、以前のダイナミック・オブジェクト・ゲートウェイ・テクノロジの強化および簡素化された形態を使用しています。オブジェクト・ゲートウェイの機能はすべて利用できるため、コードのアップグレードでは主に、特定のクラスとメソッド参照を置き換えます。以下のコードは、いくつかの一般的なアクティビティを実行するための旧式の方法と新しい方法を示しています。
アクティブなサーバ接続により Gateway オブジェクトを取得するプロセスには、以下のように 2 つの異なる ObjectScript クラスのメソッドに対する複数の呼び出しが含まれます。
set status = ##class(%Net.Remote.Service).OpenGateway("JavaGate",.GatewayInfo)
set name = GatewayInfo.Name
set port = GatewayInfo.Port
set server = GatewayInfo.Server
if ('##class(%Net.Remote.Service).IsGatewayRunning(server,port,,.status)) {
set status = ##class(%Net.Remote.Service).StartGateway(name)
}
set gateway = ##class(%Net.Remote.Gateway).%New()
set status = gateway.%Connect(server, port, "USER")
外部サーバでは、これはより簡単になります。
set gateway = $system.external.getJavaGateway()
1 つの呼び出しで、すべてが適切に設定された Gateway オブジェクトを作成し、外部サーバへの接続を自動的に開始します。外部サーバに手動でアクセスすることもできますが (“外部サーバの起動および停止” を参照)、ほとんどのアプリケーションでこれは不要です。
オブジェクト・ゲートウェイ・コードでは、プロキシを作成する前に、いくつかの異なる ObjectScript クラスへの参照と、非常に多くのサーバ固有の情報を必要としていました。
set path = ##class(%ListOfDataTypes).%New()
do path.Insert("C:\Dev\SomeClasses.jar")
do ##class(%Net.Remote.Service).OpenGateway("JavaGate",.GatewayInfo)
set gateway = ##class(%Net.Remote.Gateway).%New()
do gateway.%Connect(GatewayInfo.Server, GatewayInfo.Port, "USER",,path)
set proxy = ##class(%Net.Remote.Object).%New(gateway,"SomeClasses.ClassOne")
ここでも再び、外部サーバのコードは大幅に簡素化されています。
set gateway = getJavaGateway()
do gateway.addToPath("C:\Dev\SomeClasses.jar")
set proxy = gateway.new("SomeClasses.ClassOne")
addToPath() メソッドも、複数のクラス・パスを追加するためのより単純な方法を提供します。
以前の %ClassMethod() の代わりに、外部サーバでは新しい Gateway invoke() メソッドを使用します。
// Object Gateway
set num = ##class(%Net.Remote.Object).%ClassMethod(gateway,"Demo.ReverseGateway","factorial",num-1)
// external server
set num = gateway.invoke("Demo.ReverseGateway","factorial",num-1)