Using Remoting Proxy

While it's pretty easy to use the different forms of Haxe Remoting, the major problem is that the methods and arguments are entirely untyped. For example if you add an extra argument to one of the incoming remoting methods, you'll have to search and update all remoting calls to this method since the compiler will not be able to notice that a new argument is now needed.

RemotingProxys are a way to create a strongly-typed class that perform remoting calls behind-the-scene. Here's a small sample. Let's say we have a remoting Api that can be used, in the file Api.hx :

class Api {
  function new() { }

  public function foo(x : Int, y : Int) : Int {
    return x + y;
  }

  static var inst : Api;

  static function main() {
    inst = new Api();
    // create an incoming connection and give acces to the "inst" object
    var ctx = new haxe.remoting.Context();
    ctx.addObject("inst",inst);
    haxe.remoting.ExternalConnection.flashConnect("api",null,ctx);
  }
}

Let's compile this Api in JavaScript, by using the following HXML file :

-js api.js
-main Api

Now we have a "server" Api in Javascript, we will write a small "client" in Flash that call the Api. Here's the content of Client.hx :

class ApiProxy extends haxe.remoting.Proxy<Api> { }

class Client {
  static function main() { 
    var cnx = haxe.remoting.ExternalConnection.jsConnect("api");
    var proxy = new ApiProxy(cnx.inst);
    trace(proxy.foo(1,3));
  }
}

We can compile the client by using the following HXML file :

-swf client.swf
-main Client

When it see the extends haxe.remoting.Proxy<T>, the Haxe compiler will load the class T, list all its public instance methods, and create a class that perform the appropriate remoting calls, while providing the same method types as the original class.

For instance in our example, it is equivalent as if the programmer would have wrote :

class ApiImpl {
  var __cnx : haxe.remoting.Connection;
  public function new(c) {
     __cnx = c;
  }
  public function foo( x : Int, y : Int ) : Int {
     return __cnx.foo.call([x,y]);
  }
}

class ApiProxy extends ApiImpl {
....

But the good thing is that you don't have to duplicate the code, one time in Api.hx and one other time in Client.hx.

Now let's check that it works correctly by displaying the result of 1 + 3. For that, we will use the following HTML file :

<html>
  <head><title>Remoting Proxy</title></head>
  <body bgcolor="#eeeeee">
    <div id="haxe:trace"></div>
    <script type="text/javascript" src="api.js"></script>
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
      width="400"
      height="300"
      id="haxeFlashObject"
      align="middle">
      <param name="movie" value="client.swf"/>
      <param name="allowScriptAccess" value="always" />
      <param name="quality" value="high" />
      <param name="scale" value="noscale" />
      <param name="salign" value="lt" />
      <param name="bgcolor" value="#ffffff"/>
      <embed src="client.swf"
        bgcolor="#ffffff"
        width="400"
        height="300"
        name="haxeFlashObject"
        quality="high"
        align="middle"
        allowScriptAccess="always"
        type="application/x-shockwave-flash"
        pluginspage="http://www.macromedia.com/go/getflashplayer"
      />
    </object>
  </body>
</html>

When opening the HTML page, the result computed by JavaScript should be displayed.

Proxy Considerations

When using a RemotingProxy, you must follow several rules :

  • the Api class is loaded when the Client class is compiled. That means that you should disable some part of it if it contains js-specific types (see below).
  • all public methods are used for generating the proxy. They must be fully typed. If they reference a class, then this type will be put also in the generated proxy and the
  • you can limit the exportation of some methods by using condition compilation. For instance if you add the following to the Api.hx class, it will not be accessible from the Proxy since the Api class will be loaded in flash mode :
      #if js
      public function internalPublicMethod( x : js.Form ) {
      }
      #end

  • inheritance doesn't work, so if Api extends C, only the public methods of Api will be generated in the proxy, not the methods of C and its superclasses
  • if you have import statements in the file Api is declared, then they are also added to the generated Proxy. You can also choose which ones are included in the Proxy by using conditional compilation.

Asynchronous Proxy

If you are performing Remoting over an asynchronous Connection, then you can use the AsyncProxy which will work almost the same :

class ApiProxy extends haxe.remoting.AsyncProxy<Api> { }

Will be the same as writing the following :

class ApiImpl {
  var __cnx : haxe.remoting.AsyncConnection;
  function new(c) {
     __cnx = c;
  }
  public function foo( x : Int, y : Int, __callb : Int -> Void  ) {
     __cnx.foo.call([x,y],__callb);
  }
}

class ApiProxy extends ApiImpl {
  ...

You will now be able to use remoting, and asynchronously have a callback wait for a response.

var cnx = haxe.remoting.HttpAsyncConnection.urlConnect(url);
var myAPI = untyped new APIProxy(cnx.inst);
myAPI.foo(1,3,function (i:Int) {
    trace (i); // Should trace 4
});

Please note, for a callback to be fired, you must explicitly define a return type in your API.

For example,

// This will run, but will not fire a callback on the client
public function foo(x : Int, y : Int) {
    return x + y;
}

// This will run AND fire a callback on the client.  
// Notice the explicit return type.
public function foo(x : Int, y : Int) : Int {
    return x + y;
}

Errors

If you get Not_Found from the compiler, you should check the type parameters passed to (Async)Proxy. Try writing its full qualified name and also check if it can be reached from the classpath.

version #14405, modified 2012-07-09 17:54:11 by jason