Using Remote Servants

Remote servants are represented in the local application by objects called proxies. Proxies behave as the implementation of the remote servants they represent, thus whenever a method is called on the proxy, a corresponding method is called in the remote object. The use of proxies is the standard way to perform remote invocations in OiL.

Creation

Proxies are implicitly created whenever a remotely invoked method returns an servant. In such cases, the invoker receives a proxy to the servant instead of the servant itself. Therefore, the application usually does not deal with creation of proxies when servants are provided by methods of other proxies.

However, sometimes it is necessary to explicitly create a proxy for a remote servant. This usually is the case when the application starts up and the first proxy is created, because it cannot be obtained from a remote invocation. The solution for this case is to create a proxy from textual references using method newproxy(reference [, type]) of a broker. See section about servant references for information about how textual references are created. Textual references can be read from files (see auxiliary operation oil.readfrom) or passed as command-line parameters to applications. The code below is the complete implementation of a client application that access the LuDO server in section Registering Servants.

require "oil"

oil.main(function()
  -- initialize a LuDO ORB                     
  local broker = oil.init{flavor="ludo;base"}
  
  -- create a proxy for remote servant
  local hello = broker:newproxy(oil.readfrom("hello.ref"))
  
  -- invoke remote method
  hello:sayto("World")
end)

Some RMI protocols supported by OiL (e.g. CORBA) rely on typing information. When such protocols are used, it may be necessary to inform the interface of the referenced servant as the parameter type of operation newproxy. The possible values for this parameter depends on the RMI protocol being used.

Invocation

With respect to methods, the proxy behaves much like its referenced servant. That means that when a proxy method is called, the method with the same same is invoked on the servant. Additionally, every argument passed to the called method is available to the servant's method. Similarly, all the values returned by the servant's method are returned by the proxy method. It is worth stressing that the nature of some values used as parameter or returned values of remote invocations are suitable to subtle changes accordingly to the data transmission semantics defined by the underlying RMI protocol used by the broker.

An important feature is that proxy invocations are performed synchronously. Therefore, the invoker's execution is suspended until the results of remote method's execution are ready. However, it is possible to perform asynchronous invocations as well. This is done by a specialized proxy available as field _deferred of standard proxies. The deferred proxy behaves like a standard proxy, but every invocation immediately returns a single object denominated future. A future is an object that is used to retrieve the results of the asynchronous invocation that might only be available in a future moment. A future is an simple object that provides the following interface, which is used to access the results of an asynchronous invocation

ready()
Method that returns true if the results are already available or false otherwise.
results()
Method that waits for the completion of the remote invocation if it is not completed already, and returns the results of the asynchronous invocation. The first returned value is either true to indicated that no errors were raised or false otherwise. The other returned values are either the values returned by the remote invocation, in case of an invocation that did not raise errors, or the error raised by the remote servant. This method should never raise errors.
evaluate()
Method that waits for the completion of the remote invocation if it is not completed already, and returns the values returned by the remote invocation if no errors were raised during the execution of the remote method. If the servant raised an error during the execution of the remote invocation, then this method raises the same error in the local context.

Exceptions

The way errors are catch in Lua is by the use of function pcall(func, ...). However, this function was designed to catch errors in functions thus it provides a signature that is cumbersome to use to catch errors in method calls. As illustrated in the example below.

local ok, err = pcall(proxy.invoked_method, proxy)

One way to avoid this is to use method results() of futures, because it never raises errors. In this approach, the example above would become.

local ok, err = proxy._deferred:invoked_method():results()

One last alternative is to use the specialized proxy available in field _try denominated protected proxy. These proxy behaves like a standard proxy, but every invocation returns an additional value that indicates whether the remote method raised an error or not. Additionally, if the remote method raises some error, this error is returned as the second returned value, just after the false indicating that the error was raised. Using this last approach, the example above would become.

local ok, err = proxy._try:invoked_method()

OiL also supports the definition exception handling functions for proxies. An exception handler is a function that receives the following parameters:

proxy
object proxy that performed the operation.
exception
exception/error raised.
operation
descriptor of the operation that raised the exception.

The definition of exception handlers avoids that exceptions be raised. Therefore, if the handler do not want to consume the exception, it must re-raise it. Otherwise, the values returned by the exception handler is used as the results of the operation that raised the exception originally. The exception handler of a proxy is identified by field __exceptions. Additionally, a single exception handler can be defined to all proxies of a given broker by method setexcatch(hander [, type]). When the broker provides support for typing (e.g. CORBA), the parameter type is used to restrict the use of the handler to proxies of that type.

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.