It is sometimes beneficial to be able to implement proxy classes manually. This is a useful option both for client programmers and the implementers of interfaces, as it allows client interaction with remote services to be optimized while hiding added levels of complexity.
For client programmers, a typical example is where it is desirable to introduce load balancing between several remote objects when invoking operations. For example, if multiple remote objects can satisfy a request for a computationally intensive operation, a client application may wish to route each invocation to the object which is currently least busy.
For interface implementers, it is often useful to implement smart proxies in order to cache some information from a remote object locally at a client site. In our simple bank application, we may wish, for example, to cache the balance of an account at a client. Requests to obtain the balance of the account could then be immediately satisfied, provided of course that we take care to ensure that withdrawals and deposits to the account cause the cached value to be refreshed.
In this chapter, we first consider the details of how proxy objects are actually generated, and the general steps needed to implement smart proxy support for a given interface. We then proceed to consider how a simple smart proxy can be built. This example will be based on a small load balancing application.
For each IDL interface, the OrbixWeb IDL compiler generates a Java interface which defines the client view of the IDL interface. It also generates a Java proxy class, which implements proxy functionality for the methods defined in the Java interface. The proxy class gives the code for standard proxies for that IDL interfacethese proxies transmit requests to their real object and return the results they receive to the caller.
A smart proxy class is a user-defined alternative to the IDL generated proxy class. OrbixWeb implicitly constructs a standard proxy when an object reference enters the client address space1. However, a smart proxy cannot be implicitly created, so each smart proxy class depends on the implementation of a corresponding class which manufactures smart proxy objects when requested to by OrbixWeb. This class is called a smart proxy factory class.
To provide smart proxies for an IDL interface, a programmer must:
New()
method whenever it wishes to create a proxy for that interface.
Once these steps are carried out, OrbixWeb will communicate with the smart proxy factory whenever it needs to create a proxy of that interface:
_bind()
method is called.
out
or inout
parameter or a return value, or when a reference to a remote object enters an address space via an in
parameter.
_CORBA.Orbix.string_to_object()
is called with a stringified object reference for a proxy of that interface.
A chain of smart proxy factories is allowed for an IDL interface because the same IDL interface might be provided by a number of different servers in the system. It may be useful, therefore, to have different smart proxy code to handle each server, or set of servers. Each factory in turn can examine the marker and server name of the target object for which the proxy is to be created, and decide whether to create a smart proxy for it or to defer the request to the next proxy factory in the chain.
In more detail, the following steps must be carried out in order to create smart proxies:
ProxyFactory
class (defined in package IE.Iona.Orbix2.CORBA
). It should redefine the New()
method to create new smart proxy objects of the class in (1.) above; or return zero to indicate that it is not willing to create a smart proxy.
The factory manager requests each proxy factory to manufacture a new proxy via its
New()
method:
// Java // The String parameter is the full object // reference of the target object. // The return value is the new smart proxy // object. public IE.Iona.Orbix2.CORBA._ObjectRef New (String ref);One way to extract the target object's marker and server name is to simply decompose the object reference string. The format of this string is described in section 5.1.1.
If the
New()
method returns null, then OrbixWeb will try the next smart proxy factory in the chain.Examples of these smart proxy implementation steps are given in the rest of this chapter.
// IDL interface NumberCruncher { long crunch (in long number); }; interface NCManager { // Get the least loaded number cruncher: NumberCruncher getNumberCruncher (); };In this application, we assume that a number of objects exist which implement the
NumberCruncher
interface. Each of these objects is capable of exhibiting individual load characteristics (for example, this would be the case if each was located in a separate OrbixWeb server process).We also assume that an OrbixWeb server exists which implements the
NCManager
interface. The NCManager
implementation object is responsible for locating the currently least loaded NumberCruncher
and returning the corresponding object reference to the client. The client can then invoke the crunch()
operation (perhaps repeatedly) on the target object.Of course, the load on each
NumberCruncher
object changes over time. If it is valid to direct each client crunch()
invocation to any NumberCruncher
object, then the performance perceived by the client can be improved by updating the target object before each operation call. In this example, we implement a smart proxy which takes advantage of this fact to optimize the performance of the crunch()
operation.
SmartNC
, for Java proxy class NumberCruncher
. Instances of this class will store a variable which holds a default proxy for the NumberCruncher
object. This proxy variable will be updated before each call to crunch()
, and the operation invocation will then be routed via the refreshed default proxy.
// Java import IE.Iona.Orbix2._CORBA; import NumberCruncher; import _NCManagerRef; import NCManager; public class SmartNC extends NumberCruncher { // Store an NCManager proxy private _NCManagerRef theNCManager; public SmartNC (String ref) throws IE.Iona.Orbix2.CORBA.SystemException { super (ref); // Create NCManager proxy try { theNCManager = NCManager._bind (); } catch (IE.Iona.Orbix2.CORBA.SystemException se) { ... } } public int crunch (int number) throws IE.Iona.Orbix2.CORBA.SystemException { _NumberCruncherRef actNC = null; // Create default proxy for current // least busy NumberCruncher object try { actNC = theNCManager.getNumberCruncher (); } catch (IE.Iona.Orbix2.CORBA.SystemException se) { ... } // Make remote invocation return actNC.crunch (number); } }Class
SmartNC
inherits from the default proxy class generated by the IDL compiler. It therefore inherits all of the code required to make a remote invocation: if required, each SmartNC
method could make a call-up to its base class's method to make a remote call. However, this functionality is not required in this example.The constructor for the smart proxy class takes a full object reference string as a parameter. It must pass this to the constructor of its default proxy class. This call up is necessary to allow OrbixWeb to manage an internal table of available proxies.
In this case, the constructor also initialises a member variable which holds a proxy for the
NCManager
object by calling NCManager._bind()
.The
crunch()
method first obtains a default proxy for the current least loaded NumberCruncher
object by invoking NCManager.getNumberCruncher()
. The implementation of the smart proxy factory class described in Step B will prevent this invocation from creating a second smart proxy. The smart crunch()
method then simply invokes the default crunch()
on the newly created object.
Step B
The next step is to define a new proxy factory to generate our smart proxies at the appropriate time. This is fairly easyrecall that the base class for all proxy factory classes is class ProxyFactory
.
// Java import NumberCruncher; import _NCManagerRef; import NCManager; import IE.Iona.Orbix2.CORBA.ProxyFactory; import IE.Iona.Orbix2._CORBA; public class SmartNCFactory extends ProxyFactory { // Flag to indicate whether a smart proxy // or a true proxy should be created private static boolean createProxy; public SmartNCFactory () { createProxy = true; super (NumberCruncher.OrbixIRName ()); } public IE.Iona.Orbix2.CORBA._ObjectRef New(String ref) { // We only need one smart proxy to // manage the default proxies, so // allow implicit creation of a default // proxy (if a smart proxy already exists) if (createProxy == false) return null; createProxy = false; IE.Iona.Orbix2.CORBA._ObjectRef new_ref = null; // Create a smart proxy try { new_ref = new SmartNC (ref); } catch (IE.Iona.Orbix2.CORBA.SystemException se) { return null; } return new_ref; } }The member initialisation list of the constructor of class
SmartNCFactory
makes a call to the constructor of class ProxyFactory
. The parameter passed is the return value of the static method NumberCruncher.OrbixIRName()
. This automatically generated method returns a string which holds information about the IDL interface type for the proxy. The proxy and proxy factory class hierarchies are shown in Figure 22.1.
The
SmartNCFactory.New()
method is called by OrbixWeb to signal that a smart proxy can be created. OrbixWeb passes it the object reference of the real object for which the proxy is required. If the method decides to create a smart proxy, it must instantiate the smart proxy, passing it the object reference (and any other constructor parameters agreed on by the smart proxy and the smart proxy factory).NumberCruncher
. The New()
method first checks the member variable createProxy
member variable to determine if it needs to create a smart proxy. If the value of this variable is false
, then the method simply returns null
. This results in the invocation of the next smart proxy factory in the factory chain, or the creation of a default proxy object (if this is the last factory in the chain). A Sample Client
Finally, we need to declare a single instance of the new proxy factory class in the client:
// Java SmartNCFactory ncFact = new SmartNCFactory ();The inherited base class constructor will then register this new factoryentering it into the linked list of factories for interface
NumberCruncher
.A sample client which communicates via this smart proxy could be coded as follows:
// Java import NumberCruncher; import SmartNCFactory; import IE.Iona.Orbix2.CORBA.SystemException; public class Client { static public void main (String argv[]) { _NumberCruncherRef ncRef = null; _NCManagerRef ncmRef = null; SmartNCFactory ncFact = new SmartNCFactory (); int result1 = 0; int result2 = 0; int result3 = 0; try { // bind to NCManager ncmRef = NCManager._bind (); // get least loaded number cruncher ncRef = ncmRef.getNumberCruncher (); // do some calculations result1 = ncRef.crunch (100); result2 = ncRef.crunch (200); result3 = ncRef.crunch (300); } catch (SystemException se) { System.out.println ( "Number crunch failed."); System.out.println (se.toString ()); } } }The client simply binds to the
NCManager
object, from which it obtains an object reference for the currently least loaded NumberCruncher
. When this object reference enters the client address space, a smart proxy is created transparently to the client. The client invocations on operation crunch()
are then automatically routed through the smart proxy, as described earlier in this chapter.