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(); } }
Let's compile this Api in JavaScript, by using the following HXML file :
-js api.js -main Api haxe.remoting.Connection
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.Connection.jsConnect(); var proxy = new ApiProxy(cnx.Api.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
Apiclass is loaded when theClientclass 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.hxclass, it will not be accessible from the Proxy since theApiclass will be loaded inflashmode :#if js public function internalPublicMethod( x : js.Form ) { } #end
- inheritance doesn't work, so if
Api extends C, only the public methods ofApiwill be generated in the proxy, not the methods ofCand its superclasses - if you have
importstatements in the fileApiis 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 { ...