marat-gainullin / platypus-js

Apache License 2.0
9 stars 9 forks source link

Dynamic Rpc.Proxy instantiation in HTML5 client #94

Closed valeriy-maslov closed 7 years ago

valeriy-maslov commented 7 years ago

There is a situation when I need to instantiate Rpc.Proxy passing var, not string literal. Like this:

    //...some calculations inside function...
   var module = 'SomeModule'; // This module is getting as a result of calculations
   return new Rpc.Proxy(module); 
   //This function must return Rpc.Proxy instance 
   //for different modules depending on calculations

This is fails with this error:

26.07.2016 15:46:09 SEVERE  (String) : No server module proxy for module: ../stubs/StatelessStub    
com.google.gwt.core.client.JavaScriptException: (String) : No server module proxy for module: ../stubs/StatelessStub
    at Unknown.Vdc(pwc-0.js)
    at Unknown.kAb(pwc-0.js)
    at Unknown.bAb(pwc-0.js)
    at Unknown.nAb(pwc-0.js)
    at Unknown.iAb(pwc-0.js)
    at Unknown.Gzb(pwc-0.js)
    at Unknown.Jzb(pwc-0.js)
    at Unknown.eval(pwc-0.js)
    at Unknown.d(pwc-0.js)

Trying to fix this I did some research. So, in usual situations module name passes into Rpc.Proxy constructor as string literal. It seems Platypus analyze source code for this stuff and sends HTTP GET request back to server with __type=12 and module name as parameters.

This request type is defined like this in Platypus.js sources:

public static final int rqCreateServerModule = 12;

This request is sending rightafter HTML5 client environment starts to load and returns JSON with array list of public methods of module. I assume that this request generated with Rpc.requireRemotes(), correct me if I'm wrong.

To make this more clear for ya I also explain how this exact situation was reproduced in my code. So, all used modules is client side. There is a module which doing some stuff and must return different module implementations depending on previous results.

define(['moduleA','moduleB'],function(moduleA,moduleB) {
    function calc() {
        //... doing magic stuff ...
        if (condition) {
            return moduleA;
        } else {
            return moduleB;
        }
    }

    return calc();
});

Modules A and B have kinda complicated logic :) There is some dynamic properties which actually can be represented as simple JavaScript Function() or much more complicated Rpc.Proxy for some server module. In JS it looks like this:

define(['rpc'],function(PlatypusRPC) {

    function getImpl() {
        switch (arguments.length) {
            case 1: return new PlatypusRPC.Proxy(arguments[0]);

        }
    }

    var module = {
        getImpl: getImpl
    };

    return module;
});

I actually know your next question :)

Why the hell it is so difficult?

Well, the explanation will be too long. So I'll write only one phrase: ass-kicking microservices :)

marat-gainullin commented 7 years ago

Oh, it is completely okay. In this situation you should use

PlatypusRPC.requireRemotes(arguments[0], function(aAlreadyInstantiatedProxy){
      aAlreadyInstantiatedProxy.someMethod(..., function(){
   });
})

method. It will query server for information about server module structure, and then it will instantiate proper proxies and pass them to your callback.

valeriy-maslov commented 7 years ago

Ok, so my guess was right about it. There is another little question. Let's say I did Rpc.requireRemotes for moduleA. Will Rpc.Proxy('moduleA') work if I call it somewhere after?

marat-gainullin commented 7 years ago

Yes, it will work.

valeriy-maslov commented 7 years ago

Ok, understood, thanks :)