Reference types in the imop scheme

Class types

The imop: scheme cannot have class types, because the IMOP protocol does not support class types. This is an important design decision we made to decouple systems from each other's implementation in the Global Program Grid. Please visit this page for more information about this.

Interface types

All imop: interface types must be declared with an authority address (i.e., a host name and a port number) in the namespace and served from the host that correspond to that address. For more import on how the address work, please check the IMOP host addresses page.

An imop: interface type can extend other interface types of the same scheme, but they cannot extend interface types of other type schemes. This is because an imop: interfaces is meant to be publicly available to the network. If an imop: interface extends an interface type whose definition is available only locally, such as meso:foo.IBar, remote machines will not be able to know the complete definition of the interface, making it useless. For the same reason, all data types used in an imop: interface, such as argument types, return types, and static field types, must also belong to the imop: scheme.

For example, this is an imop: interface provided by foo.com:

namespace imop:foo.com/api;

public interface Greeting {
   void sayHello();             // interface methods are implicitly public
}

An interface at bar.com can extends the one at foo.com:

namespace imop:bar.com/api;
import imop:foo.com/api.Greeting;

public interface FriendlyGreeting extends Greeting {
   void sayHelloTo( string name );
}

An imop: interface can also have fields of imop: types, but their values are final and cannot be changed. However, if the field stores an imop: object reference, then even though the reference itself cannot be changed, the state of the object may still be altered through its methods.

For example, the following is an imop: interface that has 3 fields. The third field, DEFAULT_SERVICE, is a reference to the remote object imop:example.com/FrontDoor implementing the imop:bar.com/api.FriendlyGreeting interface:

namespace imop:foo.com/api.Parameters;
import imop:bar.com/api.FriendlyGreeting;   // an IMOP interface
import imop:example.com/FrontDoor;          // an IMOP object

public interface Parameters {
   final int WIDTH = 3;           // interface fields are also implicitly public
   final int HEIGHT = 4;
   final FriendlyGreeting DEFAULT_SERVICE = FrontDoor;
}
Object types

An object types in the imop: scheme is a singleton type whose UTL can be used to refer to its singleton instance directly. All imop: object types must also be declared with an address portion in the namespace. For example, the following is a possible implementation of the imop:example.com/FrontDoor object shown above, which will be hosted on example.com:

namespace imop:example.com/;
import imop:bar.com/api.FriendlyGreeting;
import java:java.lang.System;

public object FrontDoor implements FriendlyGreeting {

   // declared in imop:foo.com/api.Greeting
   public void sayHello() {
      System.out.println( "Hello!" );
   }

   // declared in imop:bar.com/api.FriendlyGreeting
   public void sayHelloTo( string name ) {
      System.out.println( "Hello, " + (java:string) name + "!" );
   }
}

Notice the necessary casting operator in the last expression. We will discuss it in the casting and coercion section.

An imop: object can inherit from a meso: class. However, only the methods that are explicitly defined in the imop: scheme, namely in the source code of the imop: object, are made visible to the network. Other inherited methods can be used from the meso: scheme or within the object, just not from the network. Once again, type schemes provide an additional layer of polymorphism in that different schemes may see the same object differently.

For example, assume that example.com has a meso:impl.Foo class available in its local runtime:

namespace meso:impl;
import java:java.lang.System;

public class Foo {

   public void funcFoo() {    // this method is defined in the meso scheme
      System.out.println( "Hello from Foo" );
   }
}

And it also has an object imop:example.com/api.Bar inherits from the meso:impl.Foo class:

namespace imop:example.com/api;
import meso:impl.Foo;

public object Bar extends Foo {

   public void funcBar() {    // this method is defined in the imop scheme
      funcFoo();              // calls the inherited meso method funcFoo()
   }
}

Since the funcBar() method is defined in the imop: scheme, we can call the funcBar() method from a different machine via the IMOP protocol:

namespace meso:;
import imop:example.com/api.Bar;

public class Client {

    public static void main(string[] args) {

        Bar.funcBar();        // calls funcBar() over the network
    }
}

After executing this client program, we will see the following string printed on example.com's console:

Hello from Foo

However, if we modify the client to make it call funcFoo() directly:

namespace meso:;
import imop:example.com/api.Bar;

public class Client {

    public static void main(string[] args) {

        Bar.funcFoo();    // ERROR: funcFoo() is not visible over an IMOP connection
    }
}

When executing this client, you will see the following error message:

stefan@macpro:~/local$ java -jar meso.jar -cf config.xml meso:Client
Syntax error in source /Users/stefan/local/Client.meso:
[8,13 - 19] The method funcFoo() is undefined for the type imop:example.com/api.Bar
stefan@macpro:~/local$ _

Clearly the funcFoo() method is not visible from the network even through it is declared as public. The separation between the meso: and the imop: schemes allows us to clearly distinguish methods and fields that are public to the whole world from those available only locally.

Having only singleton object types can be quite limited. In many cases, we would still need the capability to instantiate multiple objects from a common class. This can be achieved by creating a meso: class that implements imop: interfaces. An object instantiated from such a class can be used from the network through its imop: interfaces. The UTL of the object is dynamically generated when the object is passed (by reference) over an IMOP connection for the first time, and remains unchanged until the object is garbage collected or the runtime exits.

For example, this is the imop:api.org/Greeting interface provided by api.org:

namespace imop:api.org/;
 
public interface Greeting {
   void sayHello();
}

And a different host, example.com, has a meso: class implementing that interface:

namespace meso:sys;
import imop:api.org/Greeting;
import java:java.lang.System;

public class Robot implements Greeting {
   private string name;

   public Robot(string name) {
      this.name = name;
   }

   // declared in imop:api.org/Greeting
   public void sayHello() {
      System.out.println( "Hello from " + name + "!" );
   }
}

The host example.com also has an imop: object, imop:example.com/sys.RobotFactory, that can create new instances of the meso:sys.Robot class and return it to the network:

namespace imop:example.com/sys;
import imop:api.org/Greeting;
import meso:sys.Robot;

public object RobotFactory {

   public Greeting makeRobot(string name) {

      // the runtime will dynamically generate a UTL as the new object's reference
      // and return the UTL to the caller in the IMOP response message
      return new Robot( (meso:string)name );
   }
}

Now, a client program on a third machine can use the imop:example.com/sys.RobotFactory to create a new instance implementing the imop:api.org/Greeting interface:

namespace meso:;
import imop:api.org/Greeting;
import imop:example.com/sys.RobotFactory;

public class Client {

   public static void main(string[] args) {

      Greeting x = RobotFactory.makeRobot( "Wall-E" ); // x points to a remote object
      Greeting y = RobotFactory.makeRobot( "Eve" );    // y points to a remote object

      x.sayHello();
      y.sayHello();
   }
}

After executing the client, you will see the followings printed on the console of example.com. This is because objects are passed by reference; the instances never leave example.com:

Hello from Wall-E!
Hello from Eve!

The dynamically generated UTLs returned from example.com might look like this:

imop:example.com/a1b2c3d4e5

It is important to understand that as far as the meso:Client's concern, the UTL above still represents a singleton object, even though it is actually instantiated from a meso: class. There is no way to tell how the object is created over an IMOP connection. More over, there is no guarantee that the object will still be there in a few hours, or a few minutes, or even a few seconds. This is just like a web page pointed by a URL; you can link to the URL, but there is no guarantee that the page will always be there. The client program should be able to handle gracefully when a remote object is abruptly gone. This is perhaps necessary for all network programs regardless what protocol is used, be it IMOP or otherwise. One should never assume that a remote resource is reliable.

The imop:ref type

The imop:ref is a special reference type, defined in the IMOP protocol, that can hold the reference (i.e., the UTL) to any IMOP objects. This is necessary since imop: types do not inherit from a common root like meso:meso.lang.Object in the meso: scheme or java:java.lang.Object in the java: scheme that is assignable from all object instances in the scheme.

For example, the isGreetable() method bellow can take an unknown IMOP object and return true if the object implements the imop:api.org/Greeting interface:

namespace imop:foo.com/;

public object Verifier {

   public boolean isGreetable( imop:ref obj ) {   // accepts any IMOP references

      return obj instanceof imop:api.org/Greeting;
   }
}