Documentation‎ > ‎

HowTo: Use Remote Objects

The ideal distributed system has many different machines with different architectures, focuses, specialities and so on.   Some might be tiny microcontrollers, some might be home desktop machines and some might be giant server farms.  The central innovation of this fantasy is that all the communicands talk the same language in the same way.  Participating machines can securely and privately search each other's objects, look up information and trigger actions.  All of these actions can be done efficiently through the use of scheme files that allow a machine to learn a lot about remote objects.

A search of a main server for companies, for example, might yield an object that represents the company "MakingThings".  This object would be hosted on the MakingThings server and will provide an API for interacting with the company.  Logging in from work, for example, might let you first navigate to your house, but then see all the devices in your home.

Programs could be written against this background of identical interfaces which would seamless weave objects from different systems together.


This is the standard "web services" dream, except with a small enough interface that even tiny microcontrollers can participate.  Also the JSONQuery / JSONPath standards help create a JSON-based Web of Objects.  Kind of a Semantic Web for real people.

That's the big picture.  So how does this look from a Persevere point of view?

Querying for Remote Objects

Queying for objects is close to the local version,

tfs = load( "http://remote_server.com/Users/?firstName="Tooth"&lastName="Fairy" );

What comes back are objects with properties that help remote systems use them correctly.

{
id:"http://<some_server>/class/id",

property1: value,
property2: value,
property3: { "$ref", reference }
}

Perhaps the most important property is the id.  It tells us which remote system holds the instance and of what class it is.  

Using Remote Objects

Once obtained, remote objects should feel as close to local objects as possible.

ro.property = value
ro[ "property" ] = value


would cause a value to be assigned to the remote object's property.  If the value is an object, its remote reference is assigned.

variable = ro.property

would read a single property into the local variable.  Note, depending on the schema, this might come from a cache or it might result  in a remote request.

Methods, exceptions, etc. all work as you would expect.

variable = ro.method( param, param )
try { ro.reference( ) } catch ( e ) { }

So, while a straightforward GET might proceed as follows:

GET
http://localhost:8080/Customer/1
({"id":"1",
"firstName":"John",
"lastName":"Doe",
"age":41,
"friend":{"$ref":"2"}
})

A load( ) might perform quite differently, returning a reference, rather than a complete, serialized object
 
The local system can attempt to read and write properties on the object, call methods, etc. blind  - as the local code see's fit.  If the object signals it,  the local machine can go to the remote system and request the class schema for the instance.  This schema can be fairly comprehensive, outlining properties and methods, validations, parameters, etc. all of which can very helpful toward increasing efficiency.

The schema can also define which methods run locally and which remotely.  This permits a great deal of flexibility for the object designer.

Any regular properties that came back with the intial find can be used immediately by the calling program, of course, but these are only valid until the __cache_until date.  Other properties, and those that hold references to other objects, not necessarily on the same machine, can be resolved when needed and results cached if possible.

Despite all the long round trips, all these functions should be synchronous to give the easiest possible programming experience.

Schema

From Kris:

It's all through explicit configuration right now. Here is the example I
tested:

Server A (in a jslib module):
Class({id:"Customer",
    prototype: {
        add10:function(param){
            console.log("called add10 with " + param);
            return param + 10;
        },
...

Server B (in a config file):
{"id":"example.json",
"sources":[
    {
        "name":"http://localhost:8080/Customer",
        "sourceClass":"org.persvr.datasource.HttpJsonSource",
        "schema":{
            prototype:{
                remote:true,
                add10:function(){}
            },
            methods:{
                add10:{
                    runAt:"server",
                }
            }
            }
        },
...

on Server B:
js>rc = load("http://localhost:8080/Customer/1");
[object Object]
js>rc.add10(43);
53

Server A says:
INFO: called add10 with 43



Security

No object should be released to any user that does not have permission to use it.  This means that for remote references, user credentials need to be sent, authenticated then applied to authorizations.

Transactions

Transactions are as important for remote objects as for local ones.  When something fails in a transaction all changes, local and remote need to disappear.   This should look like the pjs mechanism, perhaps.

Implementation

What small things can be done to achieve as much of this vision as possible?
  • make remote rpc work - right now it does work, but the response gets mangled.
  • make remote property getting and setting work
  • make remote schema loadable, enforce checking, etc.
Later steps
  • Schema enhancement offering hints to the remote machine
  • Comet service to keep caches up to date.
Is the Persevere server pretty-much ready to go from the Stub side?  It's the proxy side that needs some time.  This problem is very like the browser - server one except the proxy side is designed for the asynch client.

Are there efficiencies that are worthwhile like getting all properties when asked for one, or getting several objects at once, etc.?


WHAT FOLLOWS IS NOT IMPLEMENTED YET: AND IS THEREFORE PURE FANTASY

Creation

Locally, of course, we say:

lo = new LocalClass( )

How would we make a remote class, locally?

ro = new RemoteObject( server, class name, parameters )
or
ro = remoteServer.new( classname, parameters )

This would give us a new object of the specified class, constructed with the supplied parameters and hosted on the specified server.  Locally objects need to get saved periodically.  We will always be sending data back to the server so this is not important.

The last option of course requires that we already have a remoteServer object knows enough to pull this off.

Perhaps if we have a schema for the object from the remote machine we can use that class definition (com.makingthings.ClassName) with the new operator?

ro = new com.makingthings.ClassName( parameters )

This makes the class name less fluid, parameterizable, etc.

Deletion

Deletion should be as simple as the rest of the API

remove( object )
or
removeServer.remove( object )


Future Ideas


There will need to be an altid mechanism that provides a second source for the object in the case the first fails.  This is particularly important as larger systems are developed where key data must be found to even get started.


Attachments (1)