Skip to main content

Mapping Specification

This chapter describes the mapping between Java objects and the Ensemble proxy classes that represent the Java objects.

Important:

Only classes, methods, and fields marked as public are imported.

This chapter describes mappings of the following types:

Package and Class Names

Package and class names are preserved when imported, except that each underscore (_) in an original Java class name is replaced with the character u and each dollar sign ($) is replaced with the character d in the Ensemble proxy class name. Both the u and the d are case-sensitive (lowercase).

Primitives

Primitive types and primitive wrappers map from Java to Ensemble as shown in the following table.

Java Ensemble
boolean %Library.BooleanOpens in a new tab
byte %Library.IntegerOpens in a new tab
char %Library.StringOpens in a new tab
double %Library.NumericOpens in a new tab
float %Library.FloatOpens in a new tab
int %Library.IntegerOpens in a new tab
long %Library.IntegerOpens in a new tab
short %Library.SmallIntOpens in a new tab
java.lang.Boolean %Library.BooleanOpens in a new tab
java.lang.Double %Library.NumericOpens in a new tab
java.lang.Float %Library.FloatOpens in a new tab
java.lang.Integer %Library.IntegerOpens in a new tab
java.lang.Long %Library.IntegerOpens in a new tab
java.lang.Short %Library.SmallIntOpens in a new tab
java.lang.String %Library.StringOpens in a new tab

Primitive Java type wrappers are mapped by default to their corresponding Ensemble data types for performance reasons. It is recommended that you always use data types whenever you are passing an argument whose type is a primitive wrapper. For example, you can call the following Java method:

public Long getOrderNumber(Integer id, Float rate)

as follows in Ensemble:

 Set id=5
 Set rate=10.0
 // order is a local Ensemble variable
 Set order=test.getOrderNumber(id,rate)

However, you are also free to import primitive wrapper types as is, then use them that way from your Ensemble code, for example:

 Set id=##class(java.lang.Integer).%New(gateway,5)
 Set rate=##class(java.lang.Float).%New(gateway,10.0)
 // order is of java.lang.Long type
 Set order=test.getOrderNumber(id,rate)

Date and Time

Date and time types map from Java to Ensemble as follows:

Java Ensemble
java.sql.Date %Library.DateOpens in a new tab
java.sql.Time %Library.TimeOpens in a new tab
java.sql.Timestamp %Library.TimeStampOpens in a new tab

Properties

The result of importing a Java class is an ObjectScript abstract class. For each Java property that does not already have corresponding getter and setter methods (imported as is), the Java Gateway engine generates corresponding ObjectScript getter and setter methods. It generates setters as setXXX, and getters as getXXX, where XXX is the property name. For example, importing a Java string property called Name results in a getter method getName() and a setter method setName(%Library.String). The gateway also generates set and get class methods for all static members.

Methods

After you perform the Java Gateway import operation, all methods in the resulting Ensemble proxy class have the same name as their Java counterparts, subject to the limitations described in the Method Names section. They also have the same number of arguments. The type for all the Ensemble proxy argument methods is %Library.ObjectHandleOpens in a new tab; the Java Gateway engine resolves types at runtime.

For example, the Java method test():

public boolean checkAddress(Person person, Address address)

is imported as:

Method checkAddress(p0 As %Library.ObjectHandle,
                    p1 As %Library.ObjectHandle) As %Library.ObjectHandle

Overloaded Methods

While Caché Basic and ObjectScript do not support overloading, you can still map overloaded Java methods to Ensemble proxy classes. This is supported through a combination of largest method cardinality and default arguments. For example, if you are importing an overloaded Java method whose different versions take two, four, and five arguments, there is only one corresponding method on the Ensemble side; that method takes five arguments, all of %ObjectHandleOpens in a new tab type. You can then invoke the method on the Ensemble side with two, four, or five arguments. The Java Gateway engine then tries to dispatch to the right version of the corresponding Java method.

While this scheme works reasonably well, avoid using overloaded methods with the same number of arguments of similar types. For example, the Java Gateway has no problems resolving the following methods:

test(int i, String s, float f)
test(Person p)
test(Person p, String s, float f)
test(int i)

However, avoid the following:

test(int i)
test(float f)
test(boolean b)
test(Object o)

Tip:

For better results using the Java Gateway, use overloaded Java methods only when absolutely necessary.

Method Names

Ensemble has a limit of 31 characters for method names. Ensure your Java method names are not longer than 31 characters. If the name length is over the limit, the corresponding Ensemble proxy method name contains only the first 31 characters of your Java method name. For example, if you have the following methods in Java:

thisJavaMethodHasAVeryVeryLongName(int i)       // 34 characters long
thisJavaMethodHasAVeryVeryLongNameLength(int i) // 40 characters long

Ensemble imports only one method with the following name:

thisJavaMethodHasAVeryVeryLongN                 // 31 characters long

The Java reflection engine imports the first one it encounters. To find out which method is imported, you can check the Ensemble proxy class code. Better yet, ensure that logging is turned on before the import operation. The Java Gateway log file contains warnings of all method names that were truncated or not imported for any reason.

Each underscore (_) in an original method name is replaced with the character u and each dollar sign ($) is replaced with the character d. Both the u and the d are case-sensitive (lowercase). If these conventions cause an unintended overlap with another method name that already exists on the Ensemble side, the method is not imported.

Finally, Ensemble class code is not case-sensitive. So, if two Java method names differ only in case, Ensemble only imports one of the methods and writes the appropriate warnings in the log file.

Static Methods

Java static methods are projected as class methods in the Ensemble proxy classes. To invoke them from ObjectScript, use the following syntax:

  // calls static Java method staticMethodName(par1,par2,...)
  Do ##class(className).staticMethodName(gateway,par1,par2,)

Constructors

You invoke Java constructors by calling %New(). The signature of %New() is exactly the same as the signature of the corresponding Java constructor, with the addition of one argument in position one: an instance of the Java Gateway. The first thing %New() does is to associate the proxy instance with the provided gateway instance. It then calls the corresponding Java constructor. For example:

 // calls Student(int id, String name) Java constructor
 Set Student=##class(javagateway.Student).%New(Gateway,29,"John Doe")

Constants

The Java Gateway projects and imports Java static final variables (constants) as Final Parameters. The names are preserved when imported, except that each underscore (_) is replaced with the character u and each dollar sign ($) is replaced with the character d. Both the u and the d are case-sensitive (lowercase).

For example, the following static final variable:

public   static   final   int   JAVA_CONSTANT   =  1;

is mapped in ObjectScript as:

Parameter JAVAuCONSTANT  As INTEGER = 1;

From ObjectScript, access the parameter as:

##class(MyJavaClass).%GetParameter("JAVAuCONSTANT"))

Java Classes

The following sections describe the particulars of using the Ensemble Java Gateway with specific types of Java classes:

Java Object Superclass (java.lang.Object)

Earlier versions of the Java Gateway did not allow the use of java.lang.Object. This release maps java.lang.Object as is. When using java.lang.Object, consider the following:

  • Primitive wrapper classes in Java, which are subclasses of java.lang.Object in Java, are mapped to Ensemble data types and are thus not subclasses of java.lang.Object in Ensemble. For details, see the Java Arrays section.

  • Although using java.lang.Object in Java provides great flexibility, it often requires much (re)casting. ObjectScript has only limited support for casting and recasting. When using java.lang.Object to point to its subclass, use the cast operation in ObjectScript to execute the methods of the subclass. Here is an example from the EJB Gateway:

     Set jndiContext=##class(javax.naming.InitialContext).%New(gateway)
     Set jndiName="PersonEJB_Sample_EJBPerson"
     Set refPerson=jndiContext.lookup(jndiName)
     Set personHomeClass=##class(java.lang.Class).forName(gateway,
         Sample.EJBPersonHome)
     Set homePerson=##class(javax.rmi.PortableRemoteObject).narrow(gateway,
        refPerson,personHomeClass)
     // here homePerson is java.lang.Object, and in Java, we would simply
     // recast it to EJBPersonHome by saying:
     //    homePerson = (EJBPersonHome) homePerson
    
     // In ObjectScript, you will need to 'recast' the method call:
     Set remotePerson=##class(Sample.EJBPersonHome)homePerson.findById(1)

Using java.lang.Object works as long as you remember you cannot recast an object per se. However, since Ensemble proxy classes are abstract classes, method invocation recasting is sufficient for most purposes.

Java Arrays

Arrays of primitive types, wrappers, data and time types, and Class types are mapped as %Library.ListOfDataTypesOpens in a new tab. Arrays of object types are mapped as %Library.ListOfObjectsOpens in a new tab. Only one level of subscripts is supported.

Java byte arrays (byte[]) are projected as %Library.GlobalBinaryStreamOpens in a new tab. Similarly, Java char arrays (char[]) are projected as %Library.GlobalCharacterStreamOpens in a new tab. This allows for a more efficient handling of byte and character arrays.

As an only exception to the general rule of pass-by-value-only semantics in the Java Gateway, you can pass byte and stream arrays either by value or by reference. Passing by reference allows changes to the byte/char stream on the Java side visible on the Ensemble side as well. A good example is the java.io.InputStream read method:

int read(byte ba[], int maxLen

which reads up to maxLen bytes into the ba byte array. For example, in Java:

byte[] ba = new byte[maxLen];
int bytesRead = inputStream.read(ba,maxLen);

The equivalent code in ObjectScript:

 Set readStream=##class(%GlobalBinaryStream).%New()
 // reserve a number of bytes since we are passing the stream by reference
 For i=1:1:50 Do readStream.Write("0")
 Set bytesRead=test.read(.readStream,50)

The following example passes a character stream by value, meaning that any changes to the corresponding Java char[] are not reflected on the Ensemble side:

 Set charStream=##class(%GlobalCharacterStream).%New()
 Do charStream.Write("Global character stream")
 Do test.setCharArray(charStream)

Java Collections Framework

Previous versions of the Java Gateway provided special treatment when importing java.util.List (and its subclasses), java.util.Map (and its subclasses) and java.util.Class. The Java Gateway imported the first two as either %Library.ListOfDataTypesOpens in a new tab or %Library.ListOfObjectsOpens in a new tab and java.util.Class as %Library.StringOpens in a new tab.

This release now imports all of the above classes “as is.” You now can use the entire Java Collections Framework “as is” in Ensemble. You can also take advantage of java.lang.Class methods. The following is a HashMap example using ObjectScript:

 Set grades=##class(java.util.HashMap).%New(gateway)
 Set x=grades.put("Biology",3.8)

 Set x=grades.put("Spanish",2.75)
 Do student.mySetGrades(grades)

 Set grades=student.myGetGrades()
 Set it=grades.keySet().iterator()
 While (it.hasNext()) {
     Set key=it.next()
     Set value=grades.get(key)
     Write " ",key," ",value,!
 }

The following example uses Class.forName and java.utilArrayList:

 Set arrayListCls=##class(java.lang.Class).forName(gateway,"java.util.ArrayList")
 Set sports=arrayListCls.newInstance()
 Do sports.add("Basketball")

 Do sports.add("Swimming")

 Set list=student.myGetFavoriteSports()
     For i=0:1:list.size()-1 {
     Write " "_list.get(i),!
 }

Recasting

ObjectScript has limited support for recasting; namely, you can recast only at a point of a method invocation. However, since all Ensemble proxies are abstract classes, this should be quite sufficient. For an example of how to recast, see the Java Object Superclass section.

Java Standard Output Redirection

The Java Gateway automatically redirects any standard Java output in the corresponding Java code to the calling Ensemble session. It collects any calls to System.out in your Java method calls and sends them to Ensemble to display in the same format as you would expect to see if you ran your code from Java. To disable this behavior and direct your output to the standard output device as designated by your Java code (in most cases that would be the console), set the following global reference in your Ensemble-enabled namespace:

 Set ^%SYS("Gateway","Remote","DisableOutputRedirect") = 1

Restrictions

Important:

Rather than aborting import, the Java Gateway engine silently skips over all the members it is unable to generate. If you repeat the import step with logging turned on, Ensemble records all skipped members (along with the reason why they were skipped) in the WARNING section of the log file.

The Java Gateway engine always makes an attempt to preserve package and method names, parameter types, etc. That way, calling an Ensemble proxy method is almost identical to calling the corresponding method in Java. It is therefore important to keep in mind Caché Basic and ObjectScript restrictions and limits while writing your Java code. In a vast majority of cases, there should be no issues at all. You might run into some Caché Basic or ObjectScript limits if, for example:

  • Your Java method names are longer than 30 characters.

  • You have 100 or more arguments.

  • You are trying to pass String objects longer than 32K.

  • You rely on the fact that Java is case-sensitive when you choose your method names.

  • You are trying to import a static method that overrides an instance method.

Check with the latest Caché Basic and ObjectScript documentation regarding any limits or restrictions. The books are:

FeedbackOpens in a new tab