Skip to main content

Working with External Languages

The $system.internal interface allows you to generate ObjectScript proxy objects that control corresponding Java, .NET, or Python target objects. A proxy object has the same set of methods and properties as the target, and each call to the proxy is echoed by the target. Communications between the proxy and the target are managed by an ObjectScript Gateway object connected to a Java, .NET, or Python External Server.

Connection between a Gateway object and an InterSystems External Server
generated description: gateway
  • The Gateway object runs in an InterSystems IRIS namespace, and manages the connection for one or more ObjectScript proxy objects.

  • The InterSystems External Server process runs in the external language environment (Java, .NET, or Python) and manages the connection for one or more target objects. Each target object runs in a separate thread spawned by the external server process.

  • A bidirectional TCP/IP connection allows the gateway object and the external server to exchange messages using a port number that uniquely identifies the external server instance.

The following sections demonstrate how to establish connections, define targets, and use proxy objects:

.

Creating a Gateway and Using a Proxy Object

The whole process of starting a connection, creating a proxy object, and calling a method can be compressed into a single statement. The following example starts the Java External Server, creates a proxy for an instance of target class java.util.Date, and calls a method to display the date:

write $system.external.getJavaGateway().new("java.util.Date").toString()
Copy code to clipboard
   Sun May 02 15:18:15 EDT 2021
Copy code to clipboard

This one line of code demonstrates the entire process of creating and using proxy objects.

Tip:

Try running this call at the InterSystems Terminal. Here are equivalent commands for C# and Python:

          write $system.external.getDotNetGateway.new("System.DateTime",0).Now
          write $system.external.getPythonGateway.new("datetime.datetime",1,1,1).now().strftime(%c)
Copy code to clipboard

The external servers are set up automatically when you install InterSystems IRIS, and will probably work without further attention (if not, see “Troubleshooting External Server Definitions” — you may have to change the path setting that identifies your preferred language platform).

The previous example compressed everything into one line, but that line is doing three important things:

  • First, it creates an ObjectScript Gateway object that encapsulates the connection between ObjectScript and the external server. Normally, you would assign the gateway to a variable:

    set javaGate = $system.external.getJavaGateway()
    Copy code to clipboard
  • Next it uses the gateway object’s new() method to create a proxy object for a specified class. Again, the object would normally be assigned to a variable:

    set dateJava = javaGate.new("java.util.Date")
    Copy code to clipboard
  • Finally, it calls a proxy object method:

    write dateJava.toString()
    Copy code to clipboard

The following examples demonstrate these steps for all three languages.

Creating Gateway Objects

There are specific gateway creation methods for the Java, .NET, and Python External Servers: getJavaGateway(), getDotNetGateway(), and getPythonGateway(). Each of these methods starts its external server in the default configuration and returns a Gateway object to manage the connection:

set javaGate = $system.external.getJavaGateway()
set netGate = $system.external.getDotNetGateway()
set pyGate = $system.external.getPythonGateway()
Copy code to clipboard

There is also a generic getGateway() method for use with customized external server configurations (see “Customizing External Server Definitions” for details) but the defaults should be sufficient for most purposes.

Creating Proxy Objects

Each gateway object has a new() method for creating proxy objects:

set dateJava = javaGate.new("java.util.Date")
set dateNet = newGate.new("System.DateTime",0)
set datePy = pyGate.new("datetime.datetime",1,1,1).now()
Copy code to clipboard

Each call to new() specifies a class name and any required arguments. This example now has three proxy objects connected through three different external servers. Each one can be treated like any other ObjectScript object. All three can be used within the same ObjectScript application.

Calling proxy object methods and properties

Now methods or properties from all three objects can be used in the same write statement:

write !,"  Java: "_dateJava.toString(),!,"  .NET: "_dateNet.Now,!, "Python: "_datePy.strftime("%c")
Copy code to clipboard
     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
Copy code to clipboard

The Java and Python examples are method calls, and the .NET example uses a property.

So far the examples have only used system classes, which are easy to demonstrate because they’re available at all times. In most cases, you will have to specify the location of a class before it can be used. The following section describes how to get a proxy for any class.

Defining Paths to Target Software

All Gateway objects can store a list of paths to software libraries (jar files, assemblies, and modules). The addToPath() method allows you to add new paths to the list. The method signature for addToPath() is:

   addToPath(path As %RawString)

The path argument can be a simple string containing a single path, or a dynamic array containing multiple paths. For example:

Adding paths to a dynamic array

The %DynamicArray class is an ObjectScript wrapper that provides a simple way to create a JSON array structure. Use the dynamic array %Set() method to add path strings to the array. The following example adds two paths to array pathlist and then passes it to the addToPath() method of a Java Gateway object:

set pathlist = []
do pathlist.%Set("/home/myhome/firstpath.jar")
do pathlist.%Set("/home/myhome/another.jar")
do javaGate.addToPath(pathlist)
Copy code to clipboard

See “Using %Set(), %Get(), and %Remove()” in Using JSON for more on dynamic arrays.

The following examples demonstrate how to specify classes for each language:

Adding paths to Java classes

For Java, the path can be a folder or a jar. The following example adds a path to someclasses.jar and then creates a proxy for an instance of someclasses.classname.

set javaGate = getJavaGateway()
do javaGate.addToPath("/home/myhome/someclasses.jar")
set someProxy = javaGate.new("someclasses.classname")
Copy code to clipboard
Adding paths to .NET classes

For .NET, the path can be a folder or an assembly. The following example adds a path to someassembly.dll and then creates a proxy for an instance of someassembly.classname

set netGate = getDotNetGateway()
do netGate.addToPath("C:\Dev\myApp\somedll.dll")
set someProxy = netGate.new("someassembly.classname")
Copy code to clipboard
Adding paths to Python classes

For Python, the path can be a module or a package. When a module is part of a package, the module path must be specified with dot notation starting at the top level package directory. For example, the path to module Foo.py could be specified in either of two ways:

  • Standard path notation if treated as a module: C:\Dev\demo\Foo.py

  • Dot notation if treated as part of package demo: C:\Dev\demo.Foo.py

The following example uses a dynamic array to add paths for two different files, both in folder C:\Dev\demo\. File Foo.py contains unpackaged class personOne, and file Bar.py contains class personTwo, which is part of package demo. Calls to new() create proxies for both classes:

set pyPaths = []
do pyPaths.%Set("C:\Dev\demo\Foo.py")
do pyPaths.%Set("C:\Dev\demo.Bar.py")

set pyGate = getPythonGateway()
do netGate.addToPath(pyPaths)
set fooProxy = pyGate.new("Foo.personOne")
set barProxy = pyGate.new("demo.Bar.personTwo")
Copy code to clipboard

It is also possible to assign targets for modules or packages that do not have classes. See “Specifying Python Targets” at the end of this section for more information.

Note:

The path argument can also be specified as an instance of %Library.ListOfDataTypes containing multiple path strings, but dynamic arrays are recommended for ease of use.

Specifying Python Targets

The syntax for specifying a Python target differs depending on whether or not the target is in a package, in a class, both, or neither. The following examples show the path as it should be specified in addToPath() and the class name as specified in new().

In the examples with classes, person is the main class and company is a class that it imports. The imported class does not have to be specified even if it is in a separate file.

You can also assign targets for modules or packages that do not have classes, but they are effectively limited to static methods and properties (since you can`t have class instance methods without a class).

no package, no class

Modules with no package and no class use the file name. Note that the argument for new() is the filename followed by a period, indicating that there is no class:

do gw.addToPath(/rootpath/noclass.py")
proxy = gw.new("noclass."))
Copy code to clipboard
no package, two classes in one file

Main class person and imported class company are both in file /rootpath/onefile.py.

do gw.addToPath(/rootpath/onefile.py")
proxy = gw.new("onefile.person")
Copy code to clipboard
no package, two classes in separate files

Main class person and imported class company are in separate files within /rootpath:

do gw.addToPath(/rootpath/person.py")
proxy = gw.new("person.person")
Copy code to clipboard
package demo, no class

Packages with no classes use the package name and file name. The actual path for the file in this example is /rootpath/demo/noclass.py. Note that the argument for new() ends with a period, indicating that there is no class:

do gw.addToPath(/rootpath/demo.noclass.py")
proxy = gw.new("demo.noclass.")
Copy code to clipboard
package demo, two classes in one file

Main class person and imported class company are both in file /rootpath/demo/onefile.py:

do gw.addToPath(/rootpath/demo.onefile.py")
proxy = gw.new("demo.onefile.person")
Copy code to clipboard
package demo, two classes in separate files

Main class person and imported class company are in separate files in /rootpath/demo:

do gw.addToPath(/rootpath/demo.person.py")
proxy = gw.new("demo.person.person")
Copy code to clipboard
Feedback