Additional Features
CORBA brokers make use of typing information to provide additional features, which facilitate some aspects of distributed programming using ORBs. Basically, these features help to reduce the interference of the broker in the application. Additionally, CORBA brokers also provide an additional API for manipulation of typing information, as described further in this section.
Implicit Servants
CORBA brokers allow that plain objects be used whenever an object reference is required. For example, suppose the server below that create file objects, which are used to access files locally stored in the host the server is running.
local oo = require "loop.base" local oil = require "oil" oil.main(function() local broker = oil.init() broker:loadidl[[ interface File { typedef sequenceContents; enum AccessMode { read, write }; exception AccessError { string error; }; Contents read(in long size) raises (AccessError); void write(in Contents data) raises (AccessError); void close(); }; interface FileSystem { File open(in string path, in File::AccessMode mode) raises (File::AccessError); }; ]] File = oo.class() function File:read(size) local data, err = self.handler:read(size) if data==nil then error{"IDL:File/AccessError:1.0",error=err} end return data end function File:write(data) local ok, err = self.handler:write(data) if ok==nil then error{"IDL:File/AccessError:1.0",error=err} end end function File:close() self.handler:close() end system = {} -- singleton object, no need for a class function system:open(path, mode) local file, err = io.open(self.path, mode:sub(1, 1)) if file then error{"IDL:File/AccessError:1.0",error=err} end file = File{ handler = file } return file -- implicitly created servant end oil.writeto("fs.ior", broker:tostring( broker:newservant(system, "FileSystem"))) broker:run() end)
Note that IDL operation FileSystem::open
returns an object reference that implements interface File
.
However, its implementation returns a simple object instead of a servant (i.e., an object reference).
In this case, the CORBA broker implicitly registers the returned object as a servant with interface File
, which is the interface of the returned value.
Implicitly created servants reduce the need for additional code related to CORBA issues.
However, there are some drawback that must be observed.
First, implicitly created servants are registered with automatic keys and the interface they implement is the type of the formal parameter they are being coerced to.
In the example above, this means that the object returned by method open
will be registered with interface File
regardless whether the object implements a more specific interface.
To solve this problem, each object that may be used in the implicit creation of a servant may provide either field __objkey
or __type
to informs the object key and the interface name or ID of the servant, respectively.
Alternatively, the field may also be provided by the object's metatable.
The example below, illustrates this.
local oo = require "loop.base" local oil = require "oil" oil.main(function() local broker = oil.init() broker:loadidl[[ interface Element { readonly attribute path; }; interface File : Element { typedef sequenceContents; enum AccessMode { read, write }; exception AccessError { string error; }; interface Handler { Contents read(in long size) raises (AccessError); void write(in Contents data) raises (AccessError); void close(); }; Handler open(in AccessMode size) raises (AccessError); }; sequence FileSeq; interface Directory : Element { FileSeq contents(); } interface FileSystem { Element search(in string path); }; ]] File = oo.class{ -- implementation of interface 'File' } Directory = oo.class{ -- implementation of interface 'Directory' } system = {} -- singleton object, no need for a class function system:search(path) local kind, errmsg = lfs.attributes(path, "mode") if kind == "file" then return File{ -- implicitly created servant path = path, __type = "File", } elseif kind == "directory" then return Directory{ -- implicitly created servant path = path, __type = "Directory", } end end oil.writeto("fs.ior", broker:tostring( broker:newservant(system, "FileSystem"))) broker:run() end)
In the example above, method FileSystem::search
return an object reference of "abstract" interface Element
.
However, the actual objects referenced supports either interface File
or Directory
.
Therefore, they define field __type
to tell OiL which interface they should be registered with.
A second observation with implicitly created servants is that if the same object is used twice implicitly create a servant, only one servant is created. This is because, whenever the same object is registered with the same key (automatic or nor) and interface, OiL reuses the previous servant.
Reference Resolution
A complementary feature for implicit servant creation is local reference resolution, which means that any object reference received by a broker that actually refers to a local servant currently registered is automatically resolved to the servant's implementation object. This means that the application receives the object originally registered as servant instead of the servant or proxy. To illustrate this feature consider a server like the one below.
require "oil" oil.main(function() local broker = oil.init() broker:loadidl[[ interface MyObject {}; interface RefHolder { attribute MyObject reference; }; interface RefHolderFactory { RefHolder create(); }; ]] Factory = {} function Factory:create() return {} -- implicitly created servant end oil.writeto("factory.ior", broker:tostring( broker:newservant(Factory, "RefHolderFactory"))) broker:run() end)
The server above is used to create objects that can hold an object reference.
Now, the following application creates a RefHolder
object to store a reference to a local object.
When the reference is retrieved the original object is restored.
require "oil" oil.main(function() local broker = oil.init() local factory = broker:newproxy(oil.readfrom("factory.ior")) local holder = factory:create() local original = {} holder:_set_reference(original) -- implicitly created servant local restored = holder:_get_reference() -- automatically resolved reference assert(rawequal(original, restored)) end)
Interface Discovery and Narrowing
Each CORBA proxy is associated to an IDL interface.
Only operations declared in this interface can be invoked using the proxy.
CORBA brokers provide method narrow
(proxy [, interface])
to create another proxy to the same object represented by proxy
, but with interface defined as parameter interface
.
For an example, see the code below that access the file server described in a previous example.
require "oil" oil.main(function() local broker = oil.init() local filesys = broker:newproxy(oil.readfrom("fs.ior")) local file = filesys:search("/usr/bin/lua") -- returns a 'Element' file = broker:narrow(file, "File") -- narrow to a 'File' file:open("read") ... end)
If narrow
is used without a second parameter, then OiL uses operation _interface
of CORBA object references to figure out its most specific interface and uses it.
Additionally, if this interface is not known, then the broker automatically imports the interface from the remote servant.
The same is true for when method newproxy
is called without the second parameter, as illustrated in the examples above.
If you look back the previous examples, you will notice that only servers load IDL specifications, while client applications just call newproxy
without specifying an interface, which causes the broker to import the interface definition using basic operation _interface
of CORBA object references.
Unfortunately, operation _interface
is optional and therefore it is not supported by every servant.
Additionally, the process of importing the entire IDL definition can be quite slow for some applications.
In these cases, the alternative is to load the IDL in the client and use the second parameter of methods newproxy
and narrow
.
Exception Support
CORBA brokers provide two additional operations that helps the use of exceptions.
First, you can create exceptions using exception's absolute name instead of repository ID using method newexcept
, as illustrated in the example below.
File = oo.class() function File:read(size) local data, err = self.handler:read(size) if data==nil then error(broker:newexcept{ "File::AccessError", error = err, }) end return data end function File:write(data) local ok, err = self.handler:write(data) if ok==nil then error(broker:newexcept{ "File::AccessError", error = err }) end end function File:close() self.handler:close() end
Other feature is that exception handlers can be defined for proxies of a specific interface with method setexcatch
(handler [, interface]).
To do so, just inform the interface's absolute name or repository ID.
Internal Interface Repository
Finally, CORBA brokers provide field types
containing an object that implements the CORBA Interface Repository and provides access to all the IDL descriptions currently loaded in the broker.