(See horb/examples/passObj for example code.)
One of the advantages of HORB is its object passing feature. Object passing can be categorized into class transfer and instance passing. An example of class transfer is when Java classes are transfered on demand when an Java applet is executed in a WWW browser. Since HORB classes are Java classes in nature, this convenient feature is inherited by HORB. Hereafter we will use the phrase object passing to indicate instance passing.
Object passing can be categorized into 1) objects passed by copying, 2) objects passed by reference, and 3) object migration. In this and the next subsections we'll explore examples of object passing by copying and by reference. Currently, HORB does not support object migration. Object migration is a feature of distributed languages that has been studied for a long time. Object migration takes place when an object continues actively communicating with other objects, even when that object is moved from one physical location (machine) to another.
Object passing by copying.
Object passing by reference.
Object migration.
An object can be: a) passed as an argument, b) returned as a return value, or c) transfered as an item of another object, either by copying or by reference. In this document the phrase "object transfer" is used to indicate the above three cases. In this subsection object transfer by object copying is treated.
Let's consider an object, ThreeD, which contains a three dimensional array of data type double. Thanks to encapsulation, only ThreeD knows how ThreeD's members are to be processed. In our example one of the methods affecting ThreeD, work(), doubles each array entry. Another method, list(), displays the array. The class ThreeD has two constructors: a null constructor, and an initializer of the array.
// ThreeD.java is a bit long because it does a useful work. package horb.examples.passObj; import java.io.*; public class ThreeD { public double data[][][]; ThreeD() {} // this is required. ThreeD(int n) { data = new double[n][n][n]; for (int i = 0; i < n; i++) // initialize array for (int j = 0; j < n; j++) for (int k = 0; k < n; k++) data[i][j][k] = i*n*n+j*n+k; } public void work() { for (int i = 0; i < data.length; i++) for (int j = 0; j < data[i].length; j++) for (int k = 0; k < data[i][j].length; k++) data[i][j][k] *= 2; // repeat gag } public void list(PrintStream ps) { for (int i = 0; i < data.length; i++) for (int j = 0; j < data[i].length; j++) for (int k = 0; k < data[i][j].length; k++) ps.print(data[i][j][k] + " "); ps.println(); } }
We want to do this work on a supercomputer that executes floating point calculation very fast. The bellow is class Server which runs on the number cruncher. The method, work(), receives a ThreeD object, calls work() method of the object, then returns the object itself. Although we could write class Server as it received an array of double, did caluculation, then returned the array, such a way should not be object oriented.
// Server.java package horb.examples.passObj; public class Server { ThreeD work(ThreeD data) { data.work(); return data; } }
Here is class Client that creates a Server object on the supercomputer, creates a ThreeD object, passes the object to the Server object, then receives back the result:
// Client.java package horb.examples.passObj; import horb.orb.*; import java.io.*; class Client { public static void main(String argv[]) { String host = (argv.length == 1) ? argv[0] : "localhost"; Server_Proxy server = new Server_Proxy(new HorbURL("horb://"+host)); ThreeD data = new ThreeD(3); System.out.println("before object passing: "); data.list(System.out); ThreeD result = server.work(data); System.out.println("after object passing: "); result.list(System.out); } }
When we compile the above source files, we must consider the
dependencies of the classes. Client uses Server, and Server uses
ThreeD. Thus, we must compile them in the reverse order. Any class
transfered remotely must have a Proxy class both on client and on server
machine.
Since our machine, serverA, is a supercomputer, let's run
the calculation on serverA.
C:> horbc ThreeD.java Server.java C:> horbc -c Client.java (copy class files to serverA) S:> horb (run horb on serverA) C:> java horb.examples.passObj.Client serverA before object passing: 0 1 2 3 4 5 6 7 8 . . . after object passing: 0 2 4 6 8 10 12 14 16 . . .
A security matter has to be considered. If any object can be transfered outside of the Server-Client classes without security, programs written in HORB are quite insecure even based on Java, which, as we're told is famous for security. Thus, HORB allows transfer of only 'non private' instance variables. If a class contains a private instance variable the HIDL compiler gives a warning when it is compiled. If you don't want to transfer a non private variable, make it transient variable. It's useful if you don't want to transfer large data.
public transient Data largeData2; // is not transfered
In addition, a remote server class might have methods that cannot be called remotely for security reason. A method ends with "_Local" is called a local method and it cannot be called remotely.
class Server { void open(String filename); // remote method void format_Local(); // local method }
By the way, the class ThreeD must have a null constructor, i.e., ThreeD(){}, if it will include other constructors due to the internal mechanisms of HORB. The Proxy class of class ThreeD instantiates class ThreeD, and the null constructor is called when the class Server receives the instance. This limitation is applicable to any class that is transfered.
(See horb/examples/complex for example code.)
In the above example, class ThreeD has only one instance variable, data[]. An object to be transfered may have any data types as instance variables. For example, scalar types, array of a scalar type, objects, arrays of objects, and so on. An array can be any dimension. Here is an example of a complex data type.
// List.java package horb.examples.complex; import java.util.Hashtable; class List { int x; List right; List left; List() {} List(int x) { this.x = x; } List(List l1, List l2) { right = l1; left = l2; } public void list(Hashtable record) {...} }
This class has three instance variables. When an instance of this class is transfered, these three instance variables will also be transfered. When the member right or left is not null, its contents will be transfered. What happen, if left contains the object itself? A loop structure is passed as a loop structure. Thus, a complex data structure can be transfered as it is without difficult programming. (Try examples in examples/complex.)
Furthermore, if a class has super classes, the instance variables of the superclasses, except java.lang.Object, will be transfered. For example, the following class has one instance variable named count and a superclass.
class CountableList extends List { public int count; }
When an instance of CountableList is transfered, as in the following example, all non-private instance variables, i.e., count, x, left, and right of the object are passed in the argument of op1(). (Try examples in examples/inheritance3.)
Caller side: .... Foo_Proxy foo = new Foo_Proxy(HorbURL(somewhere)); CountableList list = new CountableList(); foo.op1(list); // pass list to a remote object .... Callee side: class Foo { void op1(CountableList list) { ... use list ... // here is a new copy of list } }
If an object that was cast to a superclass is passed, the original object is passed. For example;
Caller side: .... Foo_Proxy foo = new Foo_Proxy(HorbURL(somewhere)); List list = new CountableList(); // casted to List foo.op1(list); // pass a casted object .... Callee side: class Foo { void op1(List list) { ... use list ... // here is an instance of CoutableList } }
The HORBC compiler has -ignoresuper option that ignores superclasses. If you don't want to pass variables in superclasses, use this option when you compile the class.
C:> horbc -ignoresuper CountableList.java